使用 Thread.Sleep 进行单元测试定时间隔操作是否不好?

发布于 2025-01-02 13:48:16 字数 331 浏览 1 评论 0原文

如果我有一个正在测试的对象,它带有一个计时器,导致在一定的时间间隔内采取一些操作,那么测试它的好方法是什么?

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

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

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

If I have an subject under test with a timer that causes some action to be taken at a timed interval, what's a good way to test that?

One method is to wrap the timer in an interface and inject it as a dependency.

However, I'd like to avoid creating yet another abstraction. It seems I can avoid that by injecting the update interval rather than the timer. Then in my test (assuming the AAA style of testing), I put a Thread.Sleep after Act and before Assert, using a very small time value so the test doesn't take long to run.

Is that a bad idea? I know it probably doesn't fully follow the principles of TDD, but it seems like there has to be a line where you stop surrounding everything with a contract and injecting it.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

勿挽旧人 2025-01-09 13:48:16

如果您的睡眠时间对测试没有任何意义,并且您可以将 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;
  }

}

如果您需要更多的反应行为(例如计时器触发等),您将不得不扩展这个想法。

If the amount you sleep doesn't have any significance on the test and you can set i to 1 millisecond then is should be fine to simply sleep for 1 millisecond in your test.

However, if you want to test complex timing behavior with timeouts and specific actions being taken at specific points in time it quickly becomes easier to abstract the concept of time and inject it as a dependency. Then your tests can operate in virtual time and execute without delay even though the code operates as if real time was passing.

A simple way to virtualize time is to use something like this:

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

}

You will have to extend this idea if you require more reactive behavior like timers firing etc.

往昔成烟 2025-01-09 13:48:16

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

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

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

Dependancy injection is the way to go to completely avoid having any "test" code within your production code (such as setting the interval just for unit testing).

However, in this case, I would use the set interval code, but use it in both unit tests and production. Have production set it to whatever, and unit tests set it to a very small amount (10ms?). Then you won't have any dead code hanging around in production.

If you set the interval, I don't see why you need the Thread.Sleep? Just have your unit test block until you get the event from the subject (or continuously poll the subject). Whatever method you are using.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文