Testability Of Date-Dependent Code
Posted by Davy Brion on October 21st, 2009
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.
October 21st, 2009 at 7:52 am
I’ve seen a code that relies on two subsequent DateTime.Now calls returns the same value. I don’t remember how it exactly looks, but it was something like this:
if(IsPaidHour(DateTime.Now))
{
price = GetCurrentPrice();
}
else
{
price = 0;
}
and GetCurrentPrice() also calls DateTime.Now and throws if called in a wrong time.
So since that I count calls to DateTimeService.
October 21st, 2009 at 9:25 am
[...] Testability Of Date-Dependent Code – Davy Brion talks about two common techniques to enable the reliable testing of Date based code either by providing the date obtaining routine with a service which can be overridden for testing, or by routing all calls for dates through a custom static method which can be provided with dates to return [...]
October 21st, 2009 at 10:23 am
Ayende uses a variation of the same technique: http://ayende.com/Blog/archive/2008/07/07/Dealing-with-time-in-tests.aspx
October 21st, 2009 at 11:20 am
This looks like a nice trick. Have you noticed any run-time penalties due to this level of indirection?
I am considering scenarios like bulking thousands (if not millions) of records where the the current time is used as a part of the key.
October 21st, 2009 at 11:36 am
the performance cost that comes with this level of indirection will definitely not be noticable. Even if you’ve got iterations of millions, you won’t notice this compared to _other_ performance penalties that you most likely will notice when dealing with that kind of volume.
or put differently: if you’re dealing with millions of records and you notice a difference because of this trick, then i will very gladly tip my hat to you sir
October 21st, 2009 at 11:53 am
I posted about this topic a while back http://journalofasoftwaredev.wordpress.com/2009/01/31/obstacles-of-unit-testing-methods-to-side-step-them-part-1-date-and-time/ using this technique it also mentions the Ayende method Karsten has linked to above. Once I get some time I should finish the rest of series off!
October 21st, 2009 at 3:29 pm
Furthermore, I would suggest to not use a singleton but pass the service to its client with dependency injection.
October 21st, 2009 at 3:34 pm
@Urs
did you read the first part of the post?
October 21st, 2009 at 3:57 pm
For years, I’ve just gone the other way – Datetime is a passed parameter
October 21st, 2009 at 4:55 pm
A DateTime service is overkill for most scenarios. We do EXACTLY what you’re doing with your DateTimeProvider, works like a champ. I’m just waiting for someone to write a test that forgets to reset the time provider, but that hasn’t happened yet.
October 21st, 2009 at 5:27 pm
Here’s a third variation for you using a nestable, thread-local-storage-based “context” class.
http://melgrubb.spaces.live.com/blog/cns!A44BB98A805C8996!263.entry
The implementation may be more “overkill”, but it makes the usage nice and simple.
October 21st, 2009 at 5:39 pm
One point I’d like to mention is that of using IDisposable to ensure that the correct/real DateTime is restored at the end of your operational context. Since posting my original article, I have create written another one introducing some open source code to address this very issue:
http://jonathan-oliver.blogspot.com/2009/10/testing-timestatic-calls-and.html
It’s very similar to Mel Grubb’s approach (which I have become aware of because of his comment above), the difference being that my approach stores the DateTime object on the thread-local store rather than DateTimeContext.
October 22nd, 2009 at 12:39 am
This is what I’m using
public class SomeClass
{
public static Func Now = () => DateTime.Now;
public void SomeMethod()
{
...
var x = Now().Hour;
...
}
}
And than I just setup SomeClass.Now in each of my tests
October 23rd, 2009 at 7:04 am
Great idea – just one problem—you can’t run multiple tests simultaneous in separate threads. How about a [ThreadStatic] dateTimeToReturn?
October 23rd, 2009 at 8:03 am
@Eric
we don’t run multiple tests simultaneously so it works… and like i said “And as long as that works, resist the urge to change it.”
once it stops working (like when switching to a multi threaded testrun) then you obviously change it