Runar Ovesen Hjerpbakk

Software Philosopher

The little test that could … not

Consider the following code:

 [TestFixture] public class BadUnitTestExample { [Test]
public void AgeMustReturnCorrectAge_Bad() { var aPerson = new PersonBad {
TimeOfBirth = new DateTime(1983, 9, 25) }; Assert.AreEqual(29, aPerson.Age); } }
public class PersonBad { public DateTime TimeOfBirth { get; set; } public int
Age { get { var today = DateTime.Today; int age = today.Year - TimeOfBirth.Year;
if (TimeOfBirth > today.AddYears(-age)) --age; return age; } } 

Run this test today and it will pass, but it has one obvious flaw. Can you spot it?

The test will fail on the same day once a year, on the person’s birthday.

“But Runar”, you say, “this only happens once a year and the test can be corrected in a couple of minutes.”

That is true, but remember that this test does not live alone. In any non trivial product where the development organization has practiced some kind of automated testing, it will be one of 10,000s. In a product of with 50,000 tests, 5 tests will fail daily if the failure rate is as low as 0.01%. An unacceptable waste of time.

A unit test is defined in The Art of Unit Testing as a test that:

  •  Is automated and repeatable.
  •  Is easy to implement.
  •  Remains for future use once it’s written.
  •  Is runnable by everyone.
  •  Is run at the push of a button.
  •  Runs quickly.

The test is automated, but it is not a proper unit test given the criteria above. The persons age is dependent on the current date, and thus its result will vary.

Luckily, making the test more robust is easy using the D in the SOLID principles: Dependency Inversion. Extract the needed date and time operations behind an interface, make the dependency on that interface explicit and inject the dependency where it is needed.

The example below shows a suggestion for such an interface, IClock. Using a stub, the test will always pass regardless of the current date. I’ve used a manual stub here, but it can just easily be created using a framework like Moq.

 [TestFixture] public class GoodUnitTestExample { [Test]
public void AgeMustReturnCorrectAge() { IClock clock = new StubClock(new
DateTime(2013, 9, 2)); var aPerson = new PersonGood(clock) { TimeOfBirth = new
DateTime(1983, 9, 8) }; Assert.AreEqual(29, aPerson.Age); } } public class
PersonGood { private readonly IClock clock; public PersonGood(IClock clock =
null) { this.clock = clock ?? new Clock(); } public DateTime TimeOfBirth { get;
set; } public int Age { get { var today = clock.Today; int age = today.Year -
TimeOfBirth.Year; if (TimeOfBirth > today.AddYears(-age)) --age; return age; } }
} public interface IClock { DateTime Today { get; } } public class Clock :
IClock { public DateTime Today { get { return DateTime.Today; } } } public class
StubClock : IClock { private readonly DateTime now; public StubClock(DateTime
now) { this.now = now; } public DateTime Today { get { return now.Date; } } } 

A unit test should never depend on externalities that you do not control. Time is a prime example, and it should be encapsulated in a suitable abstraction. The IClock-interface is a proposal, another solution is using a mature time-framework such as Noda Time where this abstraction is already available.

The code for this post can be found on Github.