我一直在阅读有关使用计时器和线程进行单元测试的问题。我找到了关于单元测试system.threading.timers的SO问题,但是我需要对system.timers.timer和一个包装类进行单元测试,这对于这个问题似乎并不顺利。
我只需要知道如何模拟计时器和/或系统时间,以便对它进行单元测试。我似乎无法在谷歌的任何地方找到它。
编辑&更新: 有意义的是,如果我通过如下包装来提取计时器,我可以生成一个计时器并使用模拟来用不同的计时器替换它。然后,相关部分将采用我在运行时注入的计时器(原始计算机,而不是模拟计算机)并测试它已经过时的事件代码。
答案 0 :(得分:23)
什么阻止你包裹这个?
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作为选择的模拟框架):
var timerFake = A.Fake<ITimer>();
var classUnderTest = new MyClass(timerFake);
// tell fake object to raise event now
timerFake.Elapsed += Raise.With<ElapsedEventArgs>(ElapsedEventArgs.Empty).Now;
// assert whatever was supposed to happen as event response, indeed did
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 ,这是一个允许您生成任何框架类型/方法的 fakes 的框架。但是,如果你想要的是模拟计时器,我会选择包装方法。
答案 1 :(得分:2)
您需要测试计时器或测试每个事件中执行的代码吗?如果是这样,您可以将该逻辑放在另一个类中,然后测试它。你可以相信计时器会调用它......