Unit testing with DateTime.Now

Unit testing time sensitive code can be tricky because unit tests should be 100% repeatable, and the same each time.  The system clock will never be the same each time the test is run, so it’s difficult to assert time-dependant values.

This has been talked about before.  Probably most notably by Ayende on “Dealing with time in tests”.  However I have a slightly different take on the situation.

Sometimes you have code that depends on the time moving on, so a static clock with a fixed time value is of no use (say for example generating some unique key values based partly on the current time).

What I want is at the top of a test to “start the clock” once, where it will then continue to act like a clock from then on.  I solved this problem as many people do – by providing a layer of abstraction from the system clock:

public interface IDateTimeProvider
{
    DateTime Now { get; }
}

The default implementation is as follows:

public class DefaultDateTimeProvider : IDateTimeProvider
{
    public DateTime Now
    {
        get { return DateTime.Now; }
    }
}

This allows classes such as this (I use Castle Windsor to wire the dependencies up when not in unit tests):

public class MyTimeDependentService
{
    public IDateTimeProvider DateTimeProvider { get; set; }

    public MyTimeDependentService()
        : this(new DefaultDateTimeProvider())
    {
    }

    public MyTimeDependentService(IDateTimeProvider dateTimeProvider)
    {
        DateTimeProvider = dateTimeProvider;
    }

    public void MyTimeDependentMethod()
    {
        var now = DateTimeProvider.Now;
        // Do stuff with the current time.
    }
\

It’s a bit more heavy-handed than Ayende’s solution, but the treacle is in the implementation of the test datetime provider:

public class TestDateTimeProvider : IDateTimeProvider
{
    private readonly DateTime initial;
    private readonly DateTime created = DateTime.Now;

    public TestDateTimeProvider(DateTime initial)
    {
        this.initial = initial;
    }

    public DateTime Now
    {
        get { return initial + (DateTime.Now – created); }
    }
}

The big win here is that you can set the “start time” of the test.  As long as you assert the current values of the datetime provider etc against the results of the method calls, you should be okay.  I realise there could be inconsistencies depending on how long the tests take to run etc, but a lot of that can be mitigated by being careful obtaining the time only once at the top of a method and referring back to it each time as in my example.

HTH. 🙂

Advertisements