Testing Against The Passage of Time

This is the fourth in a small series of posts about testing against non-determinism. In this installation, I'm going to cover the passage of time.

In my former post, I demonstrated how you can use the Provider Injection pattern to decouple your test target from a direct dependency of the current system time. In unit tests, the DateTime provider will serve up whatever value is defined by the test developer, while in production, the provider will serve up the real system time.

You can use this ability to control the apparent system time to greatly speed up time-dependent code. Although I must admit that it's not everyday that I write code that must execute for a particular length of time, I have run into this scenario from time to time, and each time I've used this principle to accelerate time during testing.

Consider this simple example:

 public void DoSomeWaiting()
 {
     TimeSpan waitTime = TimeSpan.FromSeconds(30);
     DateTime initialTime = DateTime.Now;
  
     while (DateTime.Now < initialTime.Add(waitTime))
     {
         Thread.Sleep(TimeSpan.FromMilliseconds(10));
     }
 }

In a real-life situation, you will probably have a reason for doing something like this, but the main point is that this method simply blocks for 30 seconds before it returns. Since every invocation of DoSomeWaiting takes 30 seconds, this method is a pain to unit test - particularly if you are using test-driven development (TDD). Each unit test will take at least 30 seconds, and if you have a suite of tests, you can multiply this period with the number of tests involving this method. Such a suite may easily take several minutes to execute.

For an automated build verification test, this may not be a big deal, but in TDD this is disastrous, since it totally destroys the fast-paced code-compile-test development cycle.

To amend this problem, you can alter the implementation of the method. If I add the method to the TimeConsumer class from my former post, I can implement it in this way:

 public void DoSomeWaiting()
 {
     TimeSpan waitTime = TimeSpan.FromSeconds(30);
  
     DateTime initialTime = this.dateTimeProvider_.Create("Now");
     while (this.dateTimeProvider_.Create("Now") < initialTime.Add(waitTime))
     {
         Thread.Sleep(TimeSpan.FromMilliseconds(10));
     }
 }

Instead of using DateTime.Now, I use an externally supplied ServiceProvider<DateTime> to get the 'current time' (as usual, I should point out that although I use Service Locator 2, you can also apply the principle in other ways, for example with IServiceProvider implementations).

With this implementation, I can now write a unit test that will execute orders of magnitude faster:

 [TestMethod]
 public void PerformAcceleratedWait()
 {
     ServiceProvider<DateTime> dateTimeProvider =
         new ServiceProvider<DateTime>();
     dateTimeProvider.Preset(new DateTime(2007, 5, 7), "Now");
  
     TimeConsumer tc = new TimeConsumer(dateTimeProvider);
  
     Stopwatch watch = new Stopwatch();
     watch.Start();
  
     ThreadPool.QueueUserWorkItem(delegate(object state)
     {
         Thread.Sleep(TimeSpan.FromMilliseconds(10));
         dateTimeProvider.Preset(new DateTime(2007, 5, 7, 0, 0, 31), "Now");
     });
  
     tc.DoSomeWaiting();
  
     watch.Stop();
  
     Assert.IsTrue(watch.Elapsed < TimeSpan.FromSeconds(30));
 }

Instead of having to wait for 30 seconds, this unit test executes much faster: On my system, watch.Elapsed tends to be slightly higher than 10 ms!

Being able to run a suite of tests where you can compress arbitrary periods of time down to almost no time is a great addition to your tool belt. If you are curious about this technique, I used it quite extensively for the StarShip quickstart for Service Locator 2. In those tests, I typically compress hours into a few milliseconds of test time, so it sure saved me a lot of time.