It’s come to that time of the year when summer time kicks in, also known as daylight savings, when the clocks go forward. It’s also coming up to the time of year, in the UK, that the new tax year starts. Now most of the time these items won’t make a difference but if you write code which is datetime dependent or financial software which has tax year calculations in then it maybe time for some pain.

Over the years I have experienced a number of unit tests passing without issue on a Friday but Monday morning without any changes fail. Most of the time this is due to developers using DateTime.UtcNow (or variant) inside the code being tested but the tests expecting specific values.

So how do we solve this problem? We need to remove the direct reliance on DateTime and DateTimeOffSet static properties. Once we’ve done that we can unit test for different values and make our code more robust.

Lets look at the code.

All the examples are using the preview of aspnetcore 2.1 however none of the code is 2.1 specific so will work in current 2.x aspnetcore. The same principles can be applied to full framework using a DI container.

The Service Under Test (SUT)

For this example the sut will be a very basic MVC controller which outputs next hour:

public class HomeController
{
    public ActionResult<int> Index()
    {
        return DateTimeOffset.UtcNow.AddHours(1).Hour;
    }
}

And this will return next hour based on Utc. Now this in itself is clear however can potential cause issues with testing. So how do we resolve this to make sure that we use a well known starting point for testing purposes but at run time rely on the correct value? Abstract it away.

IClock interface

By using an interface to define the properties you want. I’ve seen it called IClock and IDateTimeOffSet so it’s completely down to personal preference but the concept is the same.

public interface IClock
{
        DateTimeOffset UtcNow { get; }
}

And then create the default implementation.

public class Clock : IClock
{
     public DateTimeOffset UtcNow => DateTimeOffset.UtcNow;
}

Once the concrete class has been defined we can now update the HomeController to take in a dependency of IClock.

public class HomeController
{
    private readonly IClock _clock;

    public HomeController(IClock clock)
    {
        _clock = clock;
    }

    public ActionResult<int> Index()
    {
        return _clock.UtcNow.AddHours(1).Hour;
    }
}

Once this has been done it easily be tested but how does it run? We need to make sure it has been registered with our DI container.

In aspnetcore 2.1 (and earlier versions) this is done in the startup class in the ConfigureServices method:

public void ConfigureServices(IServiceCollection services)
{
    services.AddSingleton<IClock, Clock>();
    services.AddMvc();
}

Unit Testing

Now it’s all working for the implementation at run time we need to make sure it is tested. This can be done in a couple of ways but the concept is the same; create a mock clock implementation and use it when constructing the SUT aka the HomeController.

[Fact]
public void WithMoq()
{
    // Arrange
    var mockClock = new Mock<IClock>();
    mockClock.Setup(x => x.UtcNow).Returns(new DateTimeOffset(2020, 03, 15, 15, 0, 0, TimeSpan.Zero));

    var sut = new HomeController(mockClock.Object);

    // Act
    var result = sut.Index();

    // Assert
    Assert.Equal(16, result.Value);
}

The above uses the Moq mocking framework to create a mock instance of an IClock.

[Fact]
public void WithExplicitMockClock()
{
    // Arrange
    var sut = new HomeController(new MockClock());

    // Act
    var result = sut.Index();

    // Assert
    Assert.Equal(11, result.Value);
}

The second example uses a mock class which is easily created for example:

public class MockClock : IClock
{
    public DateTimeOffset UtcNow => new DateTimeOffset(2020, 03, 15, 10, 0, 0, TimeSpan.Zero);
}

Conclusion

In this post we have gone through how to remove code dependencies on DateTime and/or DateTimeOffset static methods to allow for easy unit testing.

Please excuse the basic idea of the example it’s the concept I’m showing not the calculation.

I’d recommend checking out NodaTime if you’re doing any datetime calculations. They’re hard so don’t re-invent the wheel!

Any questions/comments then please contact me on Twitter @WestDiscGolf