如果我有一个受测试的主题,计时器会导致某个时间间隔采取某些动作,那么测试它的好方法是什么?
一个method是将计时器包装在一个接口中并将其作为依赖项注入。
但是,我想避免创建另一个抽象。似乎我可以通过注入更新间隔而不是计时器来避免这种情况。然后在我的测试中(假设采用AAA测试方式),我在 Act 之前和 Assert 之前放置Thread.Sleep
,使用非常小的时间值进行测试不需要很长时间才能运行。
这是个坏主意吗?我知道它可能并不完全遵循TDD的原则,但似乎必须有一条线,你可以通过合同并注入它来阻止所有事情。
答案 0 :(得分:6)
如果您睡眠的数量对测试没有任何意义,您可以将i设置为1毫秒,那么在测试中只需睡1毫秒即可。
但是,如果要测试具有超时的复杂计时行为以及在特定时间点采取的特定操作,则很容易抽象出时间概念并将其作为依赖项注入。然后,您的测试可以在虚拟时间内运行,并且即使代码的运行就像实时传递一样,也可以毫不拖延地执行。
虚拟化时间的一种简单方法是使用以下内容:
interface ITimeService {
DateTime Now { get; }
void Sleep(TimeSpan delay);
}
class TimeService : ITimeService {
public DateTime Now { get { return DateTime.UtcNow; } }
public void Sleep(TimeSpan delay) { Thread.Sleep(delay); }
}
class TimeServiceStub : ITimeService {
DateTime now;
public TimeServiceStub() {
this.now = DateTime.UtcNow;
}
public DateTime Now { get { return this.now; } }
public void Sleep(TimeSpan delay) {
this.now += delay;
}
}
如果你需要更多的反应行为,比如定时器开火等,你将不得不扩展这个想法。
答案 1 :(得分:2)
依赖注入是完全避免任何"测试"生产代码中的代码(例如为单元测试设置间隔)。
但是,在这种情况下,我会使用设置间隔代码,但在单元测试和生产中都使用它。生产设置为什么,单元测试将其设置为非常小的量(10毫秒?)。然后你就不会在生产中出现任何死代码。
如果您设置间隔,我不明白为什么您需要Thread.Sleep?只有您的单元测试块,直到您从主题获得事件(或持续轮询主题)。无论你使用什么方法。