Just read a post by Jonathan Oliver where he talks about a solution he saw to tackle the problem of date-dependent code in tests. As you probably know, code that uses the current date can very easily cause testability problems. Code that accesses DateTime.Now or DateTime.UctNow can quite easily cause tests to fail for no valid reason when the tests happen to run on a certain date, on weekend days, at the end of the month, etc...
The problem obviously is that you can't override the value that DateTime.Now will return during your tests. Many people seem to resort to using some kind of DateTimeService dependency which each piece of code that needs the current date will use. Basically, something like this:
public interface IDateTimeService
{
DateTime Now { get; }
}
The implementation that will be used at runtime then looks like this:
public class DateTimeService : IDateTimeService
{
public DateTime Now
{
get { return DateTime.Now; }
}
}
Code that needs the current date simply declares a dependency on IDateTimeService and at run-time the IOC container will inject an instance of DateTimeService to fulfill the IDateTimeService dependency. At test-time, the IDateTimeService is mocked so you can easily set which date should be returned for each test.
Personally, i'm not a fan of this approach. I mean, i use the same approach for most dependencies but for simply getting the current date this is a bit too much IMO.
Instead, we simply use something like this:
public static class DateTimeProvider
{
private static DateTime? dateTimeToReturn;
public static DateTime Now
{
get { return dateTimeToReturn == null ? DateTime.Now : dateTimeToReturn.Value; }
}
public static void SetDateTimeToReturn(DateTime overriddenCurrentDateTime)
{
dateTimeToReturn = overriddenCurrentDateTime;
}
public static void ResetCurrentDateTime()
{
dateTimeToReturn = null;
}
}
And our real code simply calls DateTimeProvider.Now instead of DateTime.Now. In our tests, we call the SetDateTimeToReturn method to provide the date that we want to use for a particular test. At the end of the test, simply call the ResetCurrentDateTime method and that's it.
I generally don't like static methods but in this case, this is a very simple solution to this specific problem. And you know how the saying goes: "do the simplest thing that could possibly work". And as long as that works, resist the urge to change it.
Pingback: Reflective Perspective - Chris Alcock » The Morning Brew #459
Pingback: Testing Time—Static Calls and ISystemTime