Unit Testing DateTime – The Crazy Way
- by João Angelo
We all know that the process of unit testing code that depends on DateTime, particularly the current time provided through the static properties (Now, UtcNow and Today), it’s a PITA.
If you go ask how to unit test DateTime.Now on stackoverflow I’ll bet that you’ll get two kind of answers:
Encapsulate the current time in your own interface and use a standard mocking framework;
Pull out the big guns like Typemock Isolator, JustMock or Microsoft Moles/Fakes and mock the static property directly.
Now each alternative has is pros and cons and I would have to say that I glean more to the second approach because the first adds a layer of abstraction just for the sake of testability. However, the second approach depends on commercial tools that not every shop wants to buy or in the not so friendly Microsoft Moles. (Sidenote: Moles is now named Fakes and it will ship with VS 2012)
This tends to leave people without an acceptable and simple solution so after reading another of these types of questions in SO I came up with yet another alternative, one based on the first alternative that I presented here but tries really hard to not get in your way with yet another layer of abstraction.
So, without further dues, I present you, the Tardis. The Tardis is single section of conditionally compiled code that overrides the meaning of the DateTime expression inside a single class. You still get the normal coding experience of using DateTime all over the place, but in a DEBUG compilation your tests will be able to mock every static method or property of the DateTime class.
An example follows, while the full Tardis code can be downloaded from GitHub:
using System;
using NSubstitute;
using NUnit.Framework;
using Tardis;
public class Example
{
public Example()
: this(string.Empty) { }
public Example(string title)
{
#if DEBUG
this.DateTime = DateTimeProvider.Default;
this.Initialize(title);
}
internal IDateTimeProvider DateTime { get; set; }
internal Example(string title, IDateTimeProvider provider)
{
this.DateTime = provider;
#endif
this.Initialize(title);
}
private void Initialize(string title)
{
this.Title = title;
this.CreatedAt = DateTime.UtcNow;
}
private string title;
public string Title
{
get { return this.title; }
set
{
this.title = value;
this.UpdatedAt = DateTime.UtcNow;
}
}
public DateTime CreatedAt { get; private set; }
public DateTime UpdatedAt { get; private set; }
}
public class TExample
{
public void T001()
{
// Arrange
var tardis = Substitute.For<IDateTimeProvider>();
tardis.UtcNow.Returns(new DateTime(2000, 1, 1, 6, 6, 6));
// Act
var sut = new Example("Title", tardis);
// Assert
Assert.That(sut.CreatedAt, Is.EqualTo(tardis.UtcNow));
}
public void T002()
{
// Arrange
var tardis = Substitute.For<IDateTimeProvider>();
var sut = new Example("Title", tardis);
tardis.UtcNow.Returns(new DateTime(2000, 1, 1, 6, 6, 6));
// Act
sut.Title = "Updated";
// Assert
Assert.That(sut.UpdatedAt, Is.EqualTo(tardis.UtcNow));
}
}
This approach is also suitable for other similar classes with commonly used static methods or properties like the ConfigurationManager class.