你为什么不把这个东西包起来呢?
public interface ITimer
{
void Start(double interval);
void Stop();
event ElapsedEventHandler Elapsed;
}
这基本上是你的接口所需要的全部内容。让我们看看它的运作方式(注意,你当然可以暴露更多的 Timer
属性,但这基本上是足够的基础知识):
public class MyTimer : ITimer
{
private Timer timer = new Timer();
public void Start(double interval)
{
timer.Interval = interval;
timer.Start();
}
public void Stop()
{
timer.Stop();
}
public event ElapsedEventHandler Elapsed
{
add { this.timer.Elapsed += value; }
remove { this.timer.Elapsed -= value; }
}
}
现在,如果我们选择使用FakeItEasy作为我们的mocking框架,那么我们将如何在测试中利用它:
var timerFake = A.Fake<ITimer>();
var classUnderTest = new MyClass(timerFake);
timerFake.Elapsed += Raise.With<ElapsedEventArgs>(ElapsedEventArgs.Empty).Now;
Assert.That(classUnderTest.ReceivedEvent, Is.True);
上面的例子实际上测试的是定时器事件被触发后发生的代码。考虑以下MyClass
的代码:
public class MyClass
{
private ITimer timer;
public MyClass(ITimer timer)
{
this.timer = timer;
this.timer.Elapsed += TimerElapsedHandler;
}
public bool ReceivedEvent { get; set; }
private void TimerElapsedHandler(object sender, ElapsedEventArgs e)
{
ReceivedEvent = true;
}
}
在测试中,我们会在需要时
强制计时器启动,并检查
TimerElapsedHandler
代码是否执行,通过断言
ReceivedEvent
属性是否被设置。实际上,这个方法可能做了更多事情,但这只会改变我们进行断言的方式——想法仍然是一样的。
编辑:你也可以尝试 Moles,这是一个允许你生成任何框架类型/方法的虚假的框架。然而,如果模拟计时器是你想要的所有功能,我建议使用包装器方法。