单元测试是使用Timer类 [英] Unit testing a class that uses a Timer

查看:252
本文介绍了单元测试是使用Timer类的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经得到了有有键入 System.Windows.Forms.Timer 的私有成员的类。还有就是被称为我的每一个计时器刻度时间的私有方法。




  1. 是否值得检验的方法? (因为它是私有的)

  2. 如何测试它? (我知道我可以有我的测试类继承我要测试的类...)

  3. 我应该嘲笑我的计时器?因为如果我要测试使用一个内部定时器类,我的测试可能需要大量的时间来完成,对吧?



编辑:



事实上,该方法具有时序依赖,下面的代码:

 私人无效alertTick(对象发件人,EventArgs五){
如果(getRemainingTime()秒-1; = 0){
Display.execute(名称,WarningState.Ending,NULL);
AlertTimer.Stop();
}
,否则{
变种警告= _warnings.First(X =&X的催化剂== getRemainingTime());

如果(warning.TotalSeconds大于0)
Display.execute(姓名,WarningState.Running,警告);
}
}



正如你所看到的,如果定时器正在运行,它调用 Display.execute()从当它结束不同的参数(当剩余时间等于0)​​。请问这是设计的问题?


解决方案

  1. 您不是测试方法(私人或公共) - 您要验证类的行为。如果你还没有验证的一些行为,那么你就不能告诉它开始实施。有几种方法此行为可能会被调用 - 你的类的公共接口,或依赖某些事件。也没有必要的行为调用将改变公众接口达到的东西,有依赖性的互动也很重要

  2. 参见下面的例子。 - 它显示了如何测试这样的隐藏的行为

  3. 参见下面的例子 - 它显示了如何划分职责,注入依赖和嘲笑他们



其实你类有太多的责任 - 一个调度某个任务,而另一个 - 执行一些动作。尝试你的类的职责分割成两个独立的类。



所以,调度进入到调度:)调度API可以像:

 公共接口IScheduler 
$ { b $ b事件的EventHandler< SchedulerEventArgs>报警;
无效的start();
无效停止();
}



忘记调度现在。返回并实现你的第二个类,这将显示一些警告。让我们先去测试(MOQ):

  [测试] 
公共无效ShouldStopDisplayingWarningsWhenTimeIsOut()
{
&模拟LT; IDisplay>显示=新模拟< IDisplay>();
&模拟LT; IScheduler>调度=新的模拟< IScheduler>();

富富=新的Foo(酒吧,scheduler.Object,display.Object);
scheduler.Raise(S = GT; s.Alarm + = NULL,新SchedulerEventArgs(0));

display.Verify(D => d.Execute(酒吧,WarningState.Ending,NULL));
scheduler.Verify(S => s.Stop());
}

编写执行:

 公共类Foo 
{
私人只读IScheduler _scheduler;
私人只读IDisplay _display;
私人只读字符串_name;

公共美孚(字符串名称,IScheduler调度,IDisplay显示器)
{
_name =名称;
_display =显示;
_scheduler =调度;
_scheduler.Alarm + = Scheduler_Alarm;
_scheduler.Start();
}

私人无效Scheduler_Alarm(对象发件人,SchedulerEventArgs E)
{
_display.Execute(_name,WarningState.Ending,NULL);
_scheduler.Stop();
}
}



测试通过。写另一个问题:

  [测试] 
公共无效ShouldNotStopDisplayingWarningsWhenTimeRemains()
{
模拟< ; IDisplay>显示=新模拟< IDisplay>(MockBehavior.Strict);
&模拟LT; IScheduler>调度=新的模拟< IScheduler>(MockBehavior.Strict);
scheduler.Setup(S => s.Start());

富富=新的Foo(酒吧,scheduler.Object,display.Object);
scheduler.Raise(S => s.Alarm + =空,新SchedulerEventArgs(1));
}



测试失败。嗯,你需要为剩余时间条件:

 私人无效Scheduler_Alarm(对象发件人,SchedulerEventArgs E)
{
如果(e.RemainingTime大于0)
的回报;

_display.Execute(_name,WarningState.Ending,NULL);
_scheduler.Stop();
}

您可以继续编写测试你的类,它负责处理调度警报和执行展出一些警告。当你完成,你可以写为实现你的 IScheduler 接口。不要紧,你将如何实施计划 - 通过System.Windows.Forms.Timer或通过System.ThreadingTimer,或者一些其他的方式。


I've got a class that has a private member that has for type System.Windows.Forms.Timer. There's also a private method that is being called every time my timer ticks.

  1. Is it worth testing the method? (since it's private)
  2. How can I test it? (I know I can have my test class inheriting the class I want to test...)
  3. Should I be mocking my timer? Because if I have to test a class that uses an internal timer, my tests may take a lot of time to complete, right?

edit:

Actually, the method has a dependency on timing, here's the code:

private void alertTick(object sender, EventArgs e) {
    if (getRemainingTime().Seconds <= 0) {
        Display.execute(Name, WarningState.Ending, null);
        AlertTimer.Stop();
    }
    else {
        var warning = _warnings.First(x => x == getRemainingTime());

        if (warning.TotalSeconds > 0)
            Display.execute(Name, WarningState.Running, warning);
    }
}

As you can see, if the timer is running, it calls Display.execute() with different parameters from when it's ending (when the remaining time equals 0). Would that be a problem of design?

解决方案

  1. You are not testing methods (private or public) - you are verifying behavior of your class. And if you have not verified some behavior, then you can't tell it was implemented. There are several ways this behavior could be invoked - public interface of your class, or some event in dependency. Also not necessary that behavior invocation will change something reached by the public interface, interactions with dependencies also matter.
  2. See example below - it shows how to test such "hidden" behavior.
  3. See example below - it shows how to split responsibilities, inject dependencies and mock them.

Actually your class have too many responsibilities - one is scheduling some task, and another - executing some actions. Try to split your class into two separate classes with single responsibilities.

So, scheduling goes to scheduler :) API of scheduler could be like:

public interface IScheduler
{
    event EventHandler<SchedulerEventArgs> Alarm;
    void Start();
    void Stop();
}

Forget about scheduler for now. Return and implement your second class, which will display some warnings. Let's go test first (with Moq):

[Test]
public void ShouldStopDisplayingWarningsWhenTimeIsOut()
{
    Mock<IDisplay> display = new Mock<IDisplay>();
    Mock<IScheduler> scheduler = new Mock<IScheduler>();                      

    Foo foo = new Foo("Bar", scheduler.Object, display.Object);
    scheduler.Raise(s => s.Alarm += null, new SchedulerEventArgs(0));

    display.Verify(d => d.Execute("Bar", WarningState.Ending, null));
    scheduler.Verify(s => s.Stop());
}

Write implementation:

public class Foo
{
    private readonly IScheduler _scheduler;
    private readonly IDisplay _display;
    private readonly string _name;

    public Foo(string name, IScheduler scheduler, IDisplay display)
    {
        _name = name;
        _display = display;
        _scheduler = scheduler;
        _scheduler.Alarm += Scheduler_Alarm;
        _scheduler.Start();
    }

    private void Scheduler_Alarm(object sender, SchedulerEventArgs e)
    {
        _display.Execute(_name, WarningState.Ending, null);
        _scheduler.Stop();
    }
}

Test passes. Write another one:

[Test]
public void ShouldNotStopDisplayingWarningsWhenTimeRemains()
{
    Mock<IDisplay> display = new Mock<IDisplay>(MockBehavior.Strict);
    Mock<IScheduler> scheduler = new Mock<IScheduler>(MockBehavior.Strict);
    scheduler.Setup(s => s.Start());

    Foo foo = new Foo("Bar", scheduler.Object, display.Object);
    scheduler.Raise(s => s.Alarm += null, new SchedulerEventArgs(1));
}

Test failed. Ah, you need condition for remaining time:

private void Scheduler_Alarm(object sender, SchedulerEventArgs e)
{
    if (e.RemainingTime > 0)
        return;

    _display.Execute(_name, WarningState.Ending, null);
    _scheduler.Stop();
}

You can continue writing tests for your class, which responsible for handling scheduler alerts and executing some warnings on display. When you finish, you can write implementation for your IScheduler interface. It does not matter how you will implement scheduling - via System.Windows.Forms.Timer or via System.ThreadingTimer, or some other way.

这篇关于单元测试是使用Timer类的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆