Как запустить обработчик событий, назначенный макету? - PullRequest
14 голосов
/ 20 января 2012

Я пытаюсь запустить обработчик событий, назначенный моему макету таймера. Как я могу проверить этот приватный метод здесь?

public interface ITimer
{
    void Start();
    double Interval { get; set; }
    event ElapsedEventHandler Elapsed;
}

Клиентский класс назначает обработчик событий этому объекту. Я хочу проверить логику в этом классе.

_timer.Elapsed += ResetExpiredCounters;

И назначенный метод является личным

private void ResetExpiredCounters(object sender, ElapsedEventArgs e)
{
    // do something
}

Я хочу, чтобы этот обработчик событий был в моём макете и как-нибудь запустить его. Как я могу это сделать?

Обновление:

Я понял, что поднимаю событие, прежде чем назначить обработчик события. Я исправил это, но все еще получаю эту ошибку:

System.ArgumentException : Object of type 'System.EventArgs' cannot be converted 
to type 'System.Timers.ElapsedEventArgs'.

Я поднимаю это так:

_timer.Raise(item => item.Elapsed += null, ElapsedEventArgs.Empty);

или

_timer.Raise(item => item.Elapsed += null, EventArgs.Empty);

Оба не будут работать.

Обновление:

Вот то, что сработало для меня. Обратите внимание, что это бесполезно, если вы пытаетесь передать информацию обработчику событий, как указал Джон в комментариях. Я просто использую его, чтобы высмеивать оболочку для класса System.Timers.Timer.

_timer.Raise(item => item.Elapsed += null, new EventArgs() as ElapsedEventArgs);

В конце концов, это совсем не поможет, если вам нужно использовать аргументы события, поскольку оно всегда будет нулевым. Однако это единственный способ, поскольку ElapsedEventArgs имеет только внутренний конструктор.

Ответы [ 3 ]

11 голосов
/ 29 января 2014

ElapsedEventArgs имеет закрытый конструктор и не может быть создан.

Если вы используете:

timer.Raise(item => item.Elapsed += null, new EventArgs() as ElapsedEventArgs);

Тогда обработчик получит нулевой параметр и потеряет свое свойство SignalTime:

private void WhenTimerElapsed(object sender, ElapsedEventArgs e)
{
    // e is null.
}

В некоторых случаях этот параметр может потребоваться.

Чтобы решить эту проблему и сделать ее более тестируемой, я также создал оболочку для ElapsedEventArgs и заставил интерфейс использовать ее:

public class TimeElapsedEventArgs : EventArgs
{
    public DateTime SignalTime { get; private set; }

    public TimeElapsedEventArgs() : this(DateTime.Now)
    {
    }

    public TimeElapsedEventArgs(DateTime signalTime)
    {
        this.SignalTime = signalTime;
    }
}

public interface IGenericTimer : IDisposable
{
    double IntervalInMilliseconds { get; set; }

    event EventHandler<TimerElapsedEventArgs> Elapsed;

    void StartTimer();

    void StopTimer();
}

Реализация просто запустит свое собственное событие, получая данные из события реального таймера:

public class TimerWrapper : IGenericTimer
{
    private readonly System.Timers.Timer timer;

    public event EventHandler<TimerElapsedEventArgs> Elapsed;

    public TimeSpan Interval
    {
        get
        {
            return this.timer.Interval;
        }
        set
        {
            this.timer.Interval = value;
        }
    }

    public TimerWrapper (TimeSpan interval)
    {
        this.timer = new System.Timers.Timer(interval.TotalMilliseconds) { Enabled = false };
        this.timer.Elapsed += this.WhenTimerElapsed;
    }

    private void WhenTimerElapsed(object sender, ElapsedEventArgs elapsedEventArgs)
    {
        var handler = this.Elapsed;
        if (handler != null)
        {
            handler(this, new TimeElapsedEventArgs(elapsedEventArgs.SignalTime));
        }
    }

    public void StartTimer()
    {
        this.timer.Start();
    }

    public void StopTimer()
    {
        this.timer.Stop();
    }

    public void Dispose()
    {
        this.Dispose(true);
        GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposing)
    {
        if (!this.disposed)
        {
            if (disposing)
            {
                this.timer.Elapsed -= this.WhenTimerElapsed;
                this.timer.Dispose();
            }

            this.disposed = true;
        }
    }
}

Теперь вы можете упростить и улучшить макет этого события:

timer.Raise(item => item.Elapsed += null, new TimeElapsedEventArgs());

var yesterday = DateTime.Now.AddDays(-1);
timer.Raise(item => item.Elapsed += null, new TimeElapsedEventArgs(yesterday));

Меньше кода для написания, с ним проще работать и он полностью отделен от фреймворка.

5 голосов
/ 20 января 2012

Руководство Moq QuickStart содержит раздел о событиях. Я думаю, что вы использовали бы

mock.Raise(m => m.Elapsed += null, new ElapsedEventArgs(...));
0 голосов
/ 25 апреля 2017

В последнее время вы можете создать ElapsedEventArgs, используя отражение:

    public ElapsedEventArgs CreateElapsedEventArgs(DateTime signalTime)
    {
        var e = FormatterServices.GetUninitializedObject(typeof(ElapsedEventArgs)) as ElapsedEventArgs;
        if (e != null)
        {
            var fieldInfo = e.GetType().GetField("signalTime", BindingFlags.NonPublic | BindingFlags.Instance);
            if (fieldInfo != null)
            {
                fieldInfo.SetValue(e, signalTime);
            }
        }

        return e;
    }

Таким образом, вы можете продолжить использовать исходный делегат ElapsedEventHandler

var yesterday = DateTime.Now.AddDays(-1);
timer.Raise(item => item.Elapsed += null, CreateElapsedEventArgs(yesterday));
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...