Stories about Software


Unit Testing DateTime.Now Without Isolation

My friend Paul pointed something out to me the other day regarding my post about TDD even when you have to discard tests. I believe that this trick was taken from the book Working Effectively With Legacy Code by Michael Feathers (though I haven’t yet read this one, so I can’t be positive.

I was writing some TDD test surrounding the following production method:

and the problem I was having is that any tests that I write and check in will be good through the end of 2012 and essentially have an expiration date of Jan 1st, 2013.

What Paul pointed out is that I could refactor this to the following:

And, once I’ve done that, I can introduce the following class into my test class:

With all that in place, I can write a test that no longer expires:

In this test, I use the new class that requires me to specify the current year. It overrides the base class, which uses DateTime.Now in favor of the “current” year I’ve passed it, which has nothing to do with the non-deterministic quantity “Now”. As a result, I can TDD ’til the cows come home and check everything in so that nobody accuses me of having a Canadian girlfriend. In other words, I get to have my cake and eat it too.

  • Eric O

    The technique we’ve used for our project wsa the idea taken from Ayende’s blog ().
    We created a new class entitled SystemTime. It has one static public Func which returns a DateTimeOffset. By default, it is set to () => DateTimeOffset.Now.
    In our unit tests, we set the Func to return whatever value we would like to.
    The downside of this approach–especially if you are using an existing code base–is that you have to replace all your calls to DateTime(Offset).Now with this SystemTime.Now() call. However, if you are approaching a new project (or an manageably small existing codebase), this approach has worked well for us.

  • Eric O

    Forgot to go back & fill in the URL for Ayende’s blog entry. Here it is:

  • http://www.daedtech.com/blog Erik Dietrich

    I read through Ayende’s post, and that does seem like a cool approach. It has the interesting side effect too of being able to “way back machine” your application to an arbitrary run date without having to mess with the system clock or run in a VM. I’ll have to play with that approach and see how I like it.

  • Thibaud Desodt

    I’ve been using successfully the approach described by Mark Seemann in his book on Dependency Injection (a recommended read, by the way).

    It is described in his chapter on the DI pattern “Ambient Context”, that you can read for free here : https://www.manning.com/books/dependency-injection-in-dot-net (it’s in chapter 4, and it is described around page 124).

    Basically, the approach is to never ever use DateTime.Now or similar directly, but instead expose those via a wrapper …. but you don’t want every single class you use to have it as constructor parameters because the default implementation relying on .NET DateTime is a decent default … so you can actually define some kind of “overridable” singleton, that you mostly override only in your tests. This works also pretty well for loggers, where using a “NullLogger” makes sense by default …