[英]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.