Is unit testing timed-interval actions with Thread.Sleep bad?
Asked Answered
C

2

8

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.

Cuthburt answered 2/2, 2012 at 22:49 Comment(4)
"there has to be a line where you stop surrounding everything with a contract and injecting it" - Heresy, I say ;)Matted
Scheduling of threads in Windows is not deterministic, so doing something like this may make your test to fail at random.Gadmann
@Gadmann I'm not really trying to check anything about the scheduling of threads. Just that my timer is successfully causes a function call to happen within the expected time period + a little grace period. Would a situation like that still be vulnerable to those kind of issues?Cuthburt
@JeffBridgman, of course it would. For example if some high-priority process was running at the same time as your tests on the same computer.Gadmann
A
6

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.

Anu answered 2/2, 2012 at 22:57 Comment(0)
G
2

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.

Glabrescent answered 2/2, 2012 at 23:4 Comment(1)
I'm not quite sure how I'd code that without Thread.Sleep... I have used an AutoResetEvent before to test things, but I'm not sure how I'd do that with a mocking framework. Any examples? A code sample might help me better understand what you mean.Cuthburt

© 2022 - 2024 — McMap. All rights reserved.