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.