C# 最小起订量测试计时器
我正在建立一个计时器类,但我无法通过测试。我想测试当计时器上的时间过去时是否调用了一个方法 我有以下课程:C# 最小起订量测试计时器,c#,unit-testing,moq,C#,Unit Testing,Moq,我正在建立一个计时器类,但我无法通过测试。我想测试当计时器上的时间过去时是否调用了一个方法 我有以下课程: public class TimeOutTimer { private readonly ISubscriber _subscriber; private Timer _timer; public TimeOutTimer(ISubscriber subscriber) { _subscriber = subscriber; }
public class TimeOutTimer
{
private readonly ISubscriber _subscriber;
private Timer _timer;
public TimeOutTimer(ISubscriber subscriber)
{
_subscriber = subscriber;
}
public void Start()
{
_timer = new Timer(1000);
_timer.Start();
_timer.Elapsed += TimerOnElapsed;
}
private void TimerOnElapsed(object sender, ElapsedEventArgs elapsedEventArgs)
{
_subscriber.TimeReached();
}
}
使用最小起重量进行试验:
[Test]
public void Start_WithValidParameters_TriggersTimeReached()
{
var subscriberMock = new Mock<ISubscriber>();
var timer = new TimeOutTimer(subscriberMock.Object);
timer.Start();
subscriberMock.Verify(subscriber => subscriber.TimeReached());
}
[测试]
具有有效参数的公共无效开始\u触发器估计值()
{
var subscriberMock=new Mock();
var timer=新超时器(subscriberMock.Object);
timer.Start();
subscriberMock.Verify(subscriber=>subscriber.timereach());
}
如果我取出计时器并直接调用_subscriber.timereach(),它就会工作
我做错什么了吗?好吧,您正在处理一个异步操作。如果在处理程序中放置断点并调试代码,并且在验证行上等待拼写,那么当您跨过验证行时,您将看到处理程序确实执行了。您的问题是,您的测试在处理程序有机会触发之前完成。在您的示例中,事件应该在1000毫秒后调用,而您需要立即进行验证。显然,此时无法调用该事件 最简单的方法是在测试中加入一个线程睡眠
[Test]
public void Start_WithValidParameters_TriggersTimeReached()
{
var subscriberMock = new Mock<ISubscriber>();
var timer = new TimeOutTimer(subscriberMock.Object);
timer.Start();
Thread.Sleep(1000);
subscriberMock.Verify(subscriber => subscriber.TimeReached());
}
[测试]
具有有效参数的公共无效开始\u触发器估计值()
{
var subscriberMock=new Mock();
var timer=新超时器(subscriberMock.Object);
timer.Start();
睡眠(1000);
subscriberMock.Verify(subscriber=>subscriber.timereach());
}
然而,这并不是为基于时间的类生成单元测试的正确方法
正确的方法是创建一个ITimer
接口,然后由基于Timer
的适配器实现
然后,ITimer
将成为TimeoutTimer
的依赖项,并在构造函数中或作为属性传递给它
在测试中,您可以模拟计时器,使其与测试同步,而无需等待(例如,通过控制类手动触发事件)。您必须在测试中引发经过的事件
我怀疑是新定时器引起了麻烦。您可以尝试使其可注入,然后将其注入测试并引发事件 如果通过测试进行调试,_subscriber.timereach()是否会进入该状态?我猜不会,因为订户被模拟了。时间甚至没有被调用。我是否需要在测试中“等待”计时器激活?嗯……但在回答您的问题时,当我在没有经过事件的情况下直接调用_subscriber.timereach()时,它会按预期工作,并且验证确认调用了模拟接口方法。如果您发布了
TimeOutTimer
的完整实现,那么您还应该实现IDisposable
。事实上,您可以用新的System.Threading.Timer(state=>subscriber.timereach(),null,1000,0)替换调用新的TimeOutTimer(订阅者)
并完全消除超时时间。我知道您这样做是为了学习,但实际上“测试框架”并没有什么好处。您的测试基本上验证计时器是否引发其已用事件。就个人而言,我会把它当作阅读,而不是测试。值得测试的是您的事件处理程序是否执行了它应该执行的操作。您知道这可能会导致更多的麻烦。想象一下,如果等待10分钟,您就不能运行等待10分钟的单元测试。更好的方法是注入计时器.@AD.Net,这是我在第二段中建议的。我完全同意你的看法,那时我走对了方向。我加了睡眠,它确实通过了测试。“由基于计时器的适配器实现”有点令人困惑,我认为您在这里指的是适配器模式。我将尝试你的建议,以更优雅的方式解决这个问题。谢谢你的帮助@Dante我的意思是,你需要一个接口来模拟,但是Timer并没有实现它,所以你需要使用适配器模式来创建一个类,它实现了一个拥有你需要的接口的Timer。您显然走对了。@sklivz是的,我刚刚通读了适配器模式,在这个场景中使用它非常有意义。非常感谢;)