Saturday, 23 June 2012

Mock the operating system: timers example

Hi, today, I'd like to mention the topic of mocking the operating system and getting rid of it (especially the non-deterministic parts) as we write our specifications AKA unit tests.

The use cases

In general, there are two cases where we interact with the operating system:

  1. We tell our code to use the operating system's resources (start a thread, write to a hard drive etc.)
  2. We tell the operating system to use our code (when a timer expires, when a thread execution finishes etc.)

Timers

The topic of today's post is a combination of both above cases. Timers work as a way to say "in X milliseconds (or any other time unit), invoke Y and in the meantime, let me do my job."

The scheduling phase is where our code uses the timer and the calling back phase is where the timer uses our code. But before I show you how to properly handle this kind of situation, let's examine how things can go wrong.

A Baaad Idea

Let's assume that our specified class invokes an action periodically, each time passing in the current time. Also, let's assume that we don't care about the current time value (e.g. whether the passed time is really current) - in the real world we would, but this is just a toy example, so please bear with it. The most widely seen (but wrong - read on) way of writing a spec of a class that executes some code periodically usually consists of the following steps

  1. Make the object using the timer start this timer with a callback interval of X (so each X units of time, let's say seconds, the timer will call back)
  2. Sleep for X*Y units of time (where Y is any positive integer) + some slight tolerance
  3. After the spec wakes up from the sleep, assert that callback was called Y times

Let's quickly examine how would an implementation of such reasoning look like:

[Test]
public void ShouldPingPeriodically()
{
  int actualCallbackCount = 0;
  int interval = 4000;
  int expectedCallbackCount = 3;
  int tolerance = 1000;

  var timeNotification = 
    new CurrentTimeNotification( _ => actualCallbackCount++ );

  timeNotification.Schedule(interval);
  Thread.Sleep(interval * expectedCallbackCount + tolerance);
  timeNotification.Halt();

  Assert.AreEqual(expectedCallbackCount, actualCallbackCount);
}

This approach has one disadvantage - the interval * expectedCallbackCount is not the total time that is spent in tested code - there's one more thing we have to take into account.

There are different ways a timer can work. Either it waits for one callback to finish until it fires new wait period (this is not the case with most today's library timers, but may be with some homegrown ones). In this case, the added duration is the duration of execution of the callback passed to CurrentTimeNotification constructor. The way most of today's timers work is to fire each callback in a thread taken from thread pool. Using a thread pool boosts performance, so it's not a big penalty, yet the penalty on retrieving the thread from the pool exists. Also, usually we don't invoke our callbacks directly from the timer, because callbacks executed by the timer often require a specific signature which our passed action does not provide (if you're curious about the details, read TimerCallback delegate documentation on MSDN), so probably there is an additional code that's executed by the timer and this code invokes the action that we pass in. Usually the 1-second tolerance is more than enough to cater for all of this (by the way, remember that in case the timer fires a callback multiple times, it's really 1 second for the sum of all the calls, so each call must take under (1/expectedCallbackCount) seconds), but what if we run unit tests on a developer machine that's performing another build at the same time and is short on resources? Can the execution of the additional code that we just mentioned take more than the assumed tolerance? Of course it can! So what do we do about it?

A Better solution

The time has come to draw a line between the behaviors of our code and the operating system. In a specification of a class using a timer, we want to describe three behaviors in particular:

  1. It should create the timer passing desired method as callback
  2. It should schedule the timer for desired number of time units
  3. It should perform a desired behavior when the callback is invoked

To achieve this goal, we have to get rid of the timer by creating a thinnest possible wrapping layer. This layer has to be super thin because it's the code we're NOT going to write specs against. So, here we go - a timer wrapper:

public interface IPeriodicExecution
{
  TimerCallback Callback { get; }
  void Schedule(int milliseconds);
  void Halt();
}

public class PeriodicExecution : IPeriodicExecution
{
  private Timer timer;

  public TimerCallback Callback
  {
    get; private set;
  }

  public PeriodicExecution(TimerCallback callback)
  {
    timer = new Timer(callback);
    Callback = callback;
  }

  public void Schedule(int milliseconds)
  {
    timer.Change(0, milliseconds);
  }

  public void Halt()
  {
    timer.Change(0, Timeout.Infinite);
  }
}

As you can see, the wrapper is really thin. The only unclear element is this Callback property. We'll reveal what it is for in a moment. In the meantime, note that the timer callback is passed in a constructor. We can't mock constructors, so from the list of the three behaviors outlined in the beginning of this section, we wouldn't be able to specify the first one. In order to be able to do that, we need to introduce another thin layer - a factory!

public interface IPeriodicExecutionFactory
{
  IPeriodicExecution Create(TimerCallback callback);
}

public class PeriodicExecutionFactory
{
   public 
   IPeriodicExecution Create(TimerCallback callback)
   {
     return new PeriodicExecution(callback);
   }
}

By the way, this is not the main topic of this post, but note that because the IPeriodicExecution interface has this callback property I mentioned, we can actually test-drive this factory object in the following way:

[Test]
public void ShouldCreatePeriodicExecutionWithSpecifiedCallback()
{
  var anyCallback = Substitute.For<TimerCallback>();
  var factory = new PeriodicExecutionFactory();
  var periodicExecution = factory.Create(anyCallback);

  Assert.AreSame(anyCallback, periodicExecution.Callback);
}

Ok, now we're ready to write the specs for the three behaviors from this section start. The first one: "Should schedule the timer for desired number of time units":

[Test]
public void 
ShouldSchedulePeriodicExecutionEverySpecifiedNumberOfMilliseconds()
{
  //GIVEN
  var anyAction = Substitute.For<Action<DateTime>>();
  var factory = Substitute.For<IPeriodicExecutionFactory>();
  var periodicExecution = Substitute.For<IPeriodicExecution>();
  var anyNumberOfMilliseconds = Any.PositiveInteger();
  var notification = new CurrentTimeNotification2(anyAction, factory);

  factory.Create(notification.NotifyAboutCurrentTime).Returns(periodicExecution);

  //WHEN
  notification.Schedule(anyNumberOfMilliseconds);

  //THEN
  periodicExecution.Received().Schedule(anyNumberOfMilliseconds);
}

And the second one: "Should perform a desired behavior when the callback is invoked":

[Test]
public void 
ShouldInvokeActionWithCurrentTimeWhenTimerExpires()
{
  //GIVEN
  bool gotCalled = false;
  var factory = Substitute.For<IPeriodicExecutionFactory>();
  var notification = 
    new CurrentTimeNotification2(_ => gotCalled = true, factory);

  //WHEN
  notification.NotifyAboutCurrentTime(Any.Object());

  //THEN
  Assert.IsTrue(gotCalled);
}

And that's it. Note that this set of two specifications better describe what the actual object does, not how it works when connected to the timer. This way, they're more accurate as specifications of the class than the one using Thread.Sleep().

Summary

In this post, I tried to show how to approach timer-based designs. The same approach can be used for mocking pretty much every non-deterministic or time-based or threaded element in our implementation.

Note that this is a toy example In the real life, I'd strongly consider not creating a new timer for each new request, but pass a IPeriodicExecution instance by constructor instead of factory and reschedule it every time the request is made. This way, I'd not loose track of the started timers as I do in this example.

And remember, try to keep your design separated from your implementation.

No comments: