7

如果我有一个正在测试的主题,它带有一个计时器,它会导致在某个时间间隔内采取一些行动,那么测试它的好方法是什么?

一种方法 是将计时器包装在接口中并将其作为依赖项注入。

但是,我想避免创建另一个抽象。看来我可以通过注入更新间隔而不是计时器来避免这种情况。然后在我的测试中(假设测试的 AAA 风格),我Thread.SleepActAssert之前放置了一个,使用非常小的时间值,因此测试不需要很长时间即可运行。

这是个坏主意吗?我知道它可能没有完全遵循 TDD 的原则,但似乎必须有一条线让你停止用合同包围一切并注入它。

4

2 回答 2

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

}

如果您需要更多反应性行为(例如计时器触发等),则必须扩展此想法。

于 2012-02-02T22:57:29.187 回答
2

依赖注入是完全避免在生产代码中包含任何“测试”代码的方法(例如仅为单元测试设置间隔)。

但是,在这种情况下,我将使用设置间隔代码,但在单元测试和生产中都使用它。生产是否将其设置为任何值,而单元测试将其设置为非常小的量(10ms?)。这样你就不会在生产环境中出现任何死代码。

如果你设置了间隔,我不明白你为什么需要 Thread.Sleep?只需让您的单元测试块,直到您从主题获得事件(或不断轮询主题)。无论您使用什么方法。

于 2012-02-02T23:04:52.950 回答