簡體   English   中英

如何運行分配給模擬的事件處理程序?

[英]How can I run the event handler assigned to a mock?

我試圖觸發分配給我的計時器模擬的事件處理程序。 我如何在這里測試這個私有方法?

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);

兩者都行不通。

更新:

這對我有用。 請注意,如果您嘗試將信息傳遞給事件處理程序(如Jon在評論中指出),則無效。 我只是用它來模擬System.Timers.Timer類的包裝器。

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

最后,如果你需要使用事件參數,這根本不會有用,因為它總是為null。 但是,這是自ElapsedEventArgs只有一個內部構造函數以來的唯一方法。

ElapsedEventArgs有一個私有構造函數,無法實例化。

如果您使用:

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

然后處理程序將接收一個null參數並丟失其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));

編寫更少的代碼,更易於使用並完全與框架分離。

Moq快速入門指南中有一個關於事件的部分。 我想你會用的

mock.Raise(m => m.Elapsed += null, new ElapsedEventArgs(...));

最近解決了這個問題,你可以使用反射構造一個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));

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM