Using time? Inject a clock.

Your favourite language’s way to get the current time (e.g. new Date() in JS or DateTimeOffset.Now in C#) is lovely. It’s also a nasty global singleton, in that you can’t mock or stub it from tests.

The problem with directly referring to “the current time” from application code is that you often end up doing Thread.Sleep(...)s all over your automated test code. This makes tests slow and brittle. Now, an automated test with some busy waiting an ugly thread magic is better than no test at all, but if you have the chance, better avoid it.

I found that, in nearly all cases, it helps to inject a clock:

public interface IClock
{
    DateTimeOffset Now { get; }
}

This interface will allow you to very easily simulate time-derived behaviour. I usually use a RealClock that the application uses, and a FakeClock used by tests. They’re ridiculously simple too:

public class RealClock : IClock
{
    public DateTimeOffset Now { get { return DateTimeOffset.Now; } }
}

public class FakeClock
{
    private DateTimeOffset now;

<pre><code>public FakeClock(DateTimeOffset startTime)
{
    now = startTime;
}

public FakeClock()
{
    now = DateTimeOffset.Now;
}

/// &amp;lt;summary&amp;gt;
/// Gets or updates the clock's current time.
/// &amp;lt;/summary&amp;gt;
public DateTimeOffset Now
{
    get { return now; }
    set
    {
        if (value &amp;lt; now)
        {
            throw new InvalidOperationException(&amp;quot;Can&#039;t decrease time.&amp;quot;);
        }

        now = value;
    }
}
</code></pre>

}

The FakeClock could have been a lot simpler, but I choose to enforce that time never rewinds. Code assumes this more often than you'd think, and there's nothing wrong with that.

Using an injected clock, you get real nice test code, for anything that depends on time changes, such as UI animations or hardware simulators.

Imagine a screen that has an alarm trigger, which when activate is on for exactly 5 minutes:

[Test]
public void AlarmShouldBeVisibleFrom5Minutes()
{
    var clock = new FakeClock();
    var screen = new SomethingScreen();

<pre><code>screen.ActivateAlarm();
screen.Alarm.Enabled.ShouldBe(true);

// verify that the alarm is still going 1 second before
// the 5 minutes have passed.
clock.Now += TimeSpan.FromSeconds(299);
screen.Alarm.Enabled.ShouldBe(true);

clock.Now += TimeSpan.FromSeconds(1);
screen.Alarm.Enabled.ShouldBe(false);
</code></pre>

}

The key point is, of course, in code like clock.Now += [something];. Even if you hate mutable state, you have to accept that time is inherently mutable. Simply updating the time from automated tests allows for a pretty simple way to deal with that.

Advertisements