Testability Of Date-Dependent Code

16 commentsWritten on October 21st, 2009 by
Categories: testing

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.

  • Andrey Titov

    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.

  • Pingback: Reflective Perspective - Chris Alcock » The Morning Brew #459

  • Karsten
  • http://stefanoricciardi.net Stefano Ricciardi

    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.

  • http://davybrion.com Davy Brion

    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 :)

  • Mike

    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!

  • http://www.planetgeek.ch Urs Enzler

    Furthermore, I would suggest to not use a singleton but pass the service to its client with dependency injection.

  • http://davybrion.com Davy Brion

    @Urs

    did you read the first part of the post? :P

  • KG2V

    For years, I’ve just gone the other way – Datetime is a passed parameter

  • http://sneal.net Shawn Neal

    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.

  • http://melgrubb.spaces.live.com Mel Grubb

    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.

  • http://jonathan-oliver.blogspot.com/2009/10/testing-timestatic-calls-and.html Jonathan Oliver

    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.

  • http://seermindflow.blogspot.com SeeR

    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

  • http://thelimberlambda.com/ Eric Smith

    Great idea – just one problem—you can’t run multiple tests simultaneous in separate threads. How about a [ThreadStatic] dateTimeToReturn?

  • http://davybrion.com Davy Brion

    @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 :)

  • Pingback: Testing Time—Static Calls and ISystemTime