Our Story

Ceremony

Just Married

Inject DateTime.Now to Aid Unit Tests

by - May 28, 2015

If you have logic that relies on the current system date, it's often difficult to see how to unit test it. But by injecting a function that returns DateTime.Now we can stub the current date to be anything we want it to be.

Let's look at an example. Here we have a simple service that creates a new user instance and saves it in a database:

    public class UserService : IUserService
{
private readonly IUserData userData;

public UserService(IUserData userData)
{
this.userData = userData;
}

public void CreateUser(string username)
{
var user = new User(username, createdDateTime: DateTime.UtcNow);
userData.SaveUser(user);
}
}
Now if I want to write a unit test that checks that the correct created date is set, I have to rely on the assumption that the system date won't change between the creation of the User instance and the test assertions.

    [TestFixture]
public class UserServiceTests
{
private IUserService sut;
private IUserData userData;

[SetUp]
public void SetUp()
{
userData = MockRepository.GenerateStub<iuserdata>();
sut = new UserService(userData);
}

[Test]
public void UserServiceShouldCreateUserWithCorrectCreatedDate()
{
User user = null;

// using Rhino Mocks to grab the User instance passed to the IUserData stub
userData.Stub(x => x.SaveUser(null)).IgnoreArguments().Callback<user>(x =>
{
user = x;
return true;
});

sut.CreateUser("mike");

Assert.AreEqual(DateTime.UtcNow, user.CreatedDateTime);
}
}
But in this case, probably because Rhino Mocks is doing some pretty intensive proxying, a few milliseconds pass between the user being created and my assertions running.

Test 'Mike.Spikes.InjectingDateTime.UserServiceTests.UserServiceShouldCreateUserWithCorrectCreatedDate' failed: 
Expected: 2015-05-28 09:08:18.824
But was: 2015-05-28 09:08:18.819
InjectingDateTime\InjectDateTimeDemo.cs(75,0): at Mike.Spikes.InjectingDateTime.UserServiceTests.UserServiceShouldCreateUserWithCorrectCreatedDate()
The solution is to inject a function that returns a DateTime:

    public class UserService : IUserService
{
private readonly IUserData userData;
private readonly Func<datetime> now;

public UserService(IUserData userData, Func<datetime> now)
{
this.userData = userData;
this.now = now;
}

public void CreateUser(string username)
{
var user = new User(username, createdDateTime: now());
userData.SaveUser(user);
}
}
Now our unit test can rely on a fixed DateTime value rather than one that is changing as the test runs:

    [TestFixture]
public class UserServiceTests
{
private IUserService sut;
private IUserData userData;

// stub the system date as some arbirary date
private readonly DateTime now = new DateTime(2015, 5, 28, 10, 46, 33);

[SetUp]
public void SetUp()
{
userData = MockRepository.GenerateStub<iuserdata>();
sut = new UserService(userData, () => now);
}

[Test]
public void UserServiceShouldCreateUserWithCorrectCreatedDate()
{
User user = null;
userData.Stub(x => x.SaveUser(null)).IgnoreArguments().Callback<user>(x =>
{
user = x;
return true;
});

sut.CreateUser("mike");

Assert.AreEqual(now, user.CreatedDateTime);
}
}
And the test passes as expected.

In our composition root we inject the current system time (here as UTC):

    var userService = new UserService(userData, () => DateTime.UtcNow);
This pattern can be especially useful when we want to test business logic that relies on time passing. For example, say we want to check if an offer has expired; we can write unit tests for the case where the current (stubbed) time is both before and after the expiry time just by injecting different values into the system-under-test. Because we can stub the system time to be anything we want it to be, it makes it easy to test time based busines logic.

You May Also Like

0 comments