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

3.6k views Asked by At

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.

2

There are 2 answers

0
Martin Liversage On BEST ANSWER

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.

1
peterept On

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.