单元测试系统计时器计时器

本文关键字:计时器 测试系统 单元 | 更新日期: 2023-09-27 18:21:59

我已经阅读了关于使用定时器和线程进行单元测试的问题。我发现了关于单元测试system.threading.timers的SO问题,但我需要对system.timers.timer进行单元测试,而包装类似乎在这方面工作不太顺利。

我只需要知道如何模拟定时器和/或系统时间,以便对其进行单元测试。我似乎在谷歌上找不到这个。

编辑&更新:如果我通过如下包装来提取计时器,我可以生成一个计时器,并使用mocking将其替换为不同的计时器,这是有道理的。然后,相关的部分是获取我在运行时注入的计时器(原始计时器,而不是模拟计时器),并测试它的运行事件代码。

单元测试系统计时器计时器

是什么阻止你包装这个?

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

在测试中,我们强制计时器在需要时启动,并通过断言ReceivedEvent属性来检查TimerElapsedHandler中的代码是否已执行。事实上,这种方法可能会做得更多,但这只会改变我们做断言的方式——想法保持不变。


编辑:您也可以尝试Moles,该框架允许您生成任何框架类型/方法的fakes。然而,若你们想要的只是嘲讽定时器,我会选择包装器的方法。

您需要测试计时器还是测试在每个事件中执行的代码?如果是这样,您可以将该逻辑放入另一个类中,然后对其进行测试。你可以相信计时器会调用它…