Unit Testing DateTime – The Crazy Way
Posted
by João Angelo
on Exceptional Code
See other posts from Exceptional Code
or by João Angelo
Published on Wed, 15 Aug 2012 18:31:18 +0000
Indexed on
2012/08/27
21:52 UTC
Read the original article
Hit count: 485
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.
© Exceptional Code or respective owner