使用Moq测试计时器

时间:2012-10-05 20:10:47

标签: c# unit-testing moq

我正在构建一个计时器类,但我无法进行测试通过。我想测试在计时器上的时间过去时调用方法。

我有以下课程:

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

测试,使用Moq:

[Test]
public void Start_WithValidParameters_TriggersTimeReached()
{
    var subscriberMock = new Mock<ISubscriber>();
    var timer = new TimeOutTimer(subscriberMock.Object);

    timer.Start();

    subscriberMock.Verify(subscriber => subscriber.TimeReached());
}

如果我取出Timer并直接调用_subscriber.TimeReached(),它就可以工作。

我做错了吗?

3 个答案:

答案 0 :(得分:4)

在您的示例中,事件应在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());
}

然而,这并不是为基于时间的类生成单元测试的正确方法。

正确的方法是创建一个ITimer接口,然后由基于Timer的适配器实现。

然后ITimer成为TimeoutTimer的依赖项,并在构造函数中或作为属性传递给它。

在测试中,您可以模拟计时器,使其与测试同步而无需等待(例如,通过控制班级手动触发事件)。

答案 1 :(得分:2)

嗯,你正在处理异步操作。如果你在处理程序中放置一个断点并通过代码进行调试,并且如果你在验证行上等待一个咒语,那么当你跨过验证行时,你会看到处理程序确实执行了。你的问题是你的测试在处理程序有机会开始之前完成。

答案 2 :(得分:-1)

您必须在测试中提高Elapsed事件。

我怀疑这是导致麻烦的新计时器。您可以尝试将其注射,然后将其注入测试并提升事件。