"A" way of dealing with dates in automated tests

Dates are annoying in automated tests.  You’ll inevitably have all kinds of business rules like “if the case is unresolved and older than 15 days send a nasty email to the owner of the case to tell them to get off their duff and fix the problem already.”  Those kinds of rules really deserve automated acceptance / regression tests, but….

  • Automated tests are only useful when you can start from known inputs / test data
  • Your business rules work by comparing “today’s date” against the dates of the data using the system clock one way or another
  • The system clock is obviously going to be different tomorrow

I think you’ve got two main choices to make your automated tests with date rules:

  1. Don’t access DateTime.Now or DateTime.Today directly in your code.  Use some sort of ISystemClock service that you can mock inside your tests.  Jimmy Bogard describes that strategy here.
  2. Go ahead and access DateTime directly in your code, but use a mechanism in your test data setup that “corrects” your test data to make dates relative to the system clock.

The problem with approach #1 is that a.) you have to remember not to access DateTime.Now/Today directly, and b.) The tests aren’t as readable to the business partners, testers, and even you.  If you rely on absolute dates with some sort of mechanism that locks in “Today is 10/26/2008,” you have to look at each and every date in the test data and compare it to this arbitrary date that defines “today.”

For readability sake, I like to define test data using “TODAY”, “TODAY+#”, or “TODAY-#” to reflect relative dates.  The big advantage being that this type of date formatting clearly says “today’s date” or “31 days ago” or “2 days from now” without having to compare an absolute date against an arbitrary “now” time.

Using the StoryTeller trunk (as of my last checkin.  I’ve used this strategy with Fit/FitNesse in the past as well)

    <!-- Today's date -->
<somegrammar thedate="TODAY" />
<!-- 4 days ago -->
<somegrammar thedate="TODAY-4" />
<!-- 3 days from today -->
<somegrammar thedate="TODAY+3" />

 

About Jeremy Miller

Jeremy is the Chief Software Architect at Dovetail Software, the coolest ISV in Austin. Jeremy began his IT career writing "Shadow IT" applications to automate his engineering documentation, then wandered into software development because it looked like more fun. Jeremy is the author of the open source StructureMap tool for Dependency Injection with .Net, StoryTeller for supercharged acceptance testing in .Net, and one of the principal developers behind FubuMVC. Jeremy's thoughts on all things software can be found at The Shade Tree Developer at http://codebetter.com/jeremymiller.
This entry was posted in Test Automation Week. Bookmark the permalink. Follow any comments here with the RSS feed for this post.
  • http://koenwillemse.wordpress.com Koen

    We also use the Ayende’s approach and it works like a charm. The readability of the tests is good for as far as we can judge ;-)

  • http://house9.blogspot.com Jesse

    maybe I am being naive here, but couldn’t you just pass the DateTime (now) as a parameter to the constructor of the object under test? then in production code pass DateTime.Now but in your test pass the hard coded value to verify your business logic?

  • http://www.tobinharris.com Tobin Harris

    Nice post, agree that not using DateTime.Now etc is very valuable. You could probably use NDepend or something to enforce it on the CI too :)

    I also use Ayende’s method, and tend to add some test helper methods on a per-project basis. Stuff like:

    SystemTime.Fake(“2009-01-01″);
    SystemTime.Rewind( 4.Weeks() );
    SystemTime.FastForward( 2.Days() );

  • http://whereslou.com Louis DeJardin

    I like the clock interface myself… Especially if the interface can provide some meaningful operations that keep DateTime/TimeSpan arithmetic out of components.

    This one’s geared more for background task intervals, but you get the idea.

    public class TestClock : IClock
    {
    private DateTime _now = new DateTime(1971, 10, 14, 12, 34, 56);

    DateTime IClock.GetOffsetMoment(TimeSpan offset)
    { return _now.Add(offset); }

    bool IClock.HasMomentArrived(DateTime moment)
    { return moment <= _now; }

    public void Advance(TimeSpan offset)
    { _now = _now.Add(offset); }
    }

    Assert.That(entry.Status, Is.EqualTo(TaskStatus.Idle));
    _clock.Advance(TimeSpan.FromSeconds(25));
    Assert.That(entry.Status, Is.EqualTo(TaskStatus.Idle));
    _clock.Advance(TimeSpan.FromMinutes(3));
    Assert.That(entry.Status, Is.EqualTo(TaskStatus.Pending));

  • http://andreasohlund.blogspot.com Andreas Öhlund

    +1 for Ayendes approach (we’ve been using it with great success!)

    //set systemtime to a fixed value
    SystemTime.Now = () => new DateTime(2008, 1, 1);

  • http://omgwtftdd.blogspot.com/2009/02/testing-datetimenow-introducing-wrap.html Erik Rydeman

    Interesting post!
    The approach I’ve had the most success with is definitely alternative 1.

    What I usually do though is not assign some arbitrary time to use as a baseline but instead wrap DateTime.Now and freeze the now moment.

    Something like this is usually what it looks like:

    [SetUp]
    public void Setup(){
    TimeWarper.Now = DateTime.Now; //Freeze time!
    }

    [Test]
    public void TestSomething(){

    //myThing uses TimeWarper.Now internally
    myThing.DoStuffInvolvingTime(48);

    Assert.AreEqual(TimeWarper.Now.AddDays(2).ToString(), myThing.FutureDate.ToString());
    }

    This has the advantage of not caring what the absolute value of the date is. At the same time doing it with a static override you don’t have to do any special date declarations in your tests, which is great for readability.

    As for the case when you forget about using the wrapper, that will hopefully result in a broken test and is easily fixable. :)

    I blogged a bit about it here a while ago:
    http://omgwtftdd.blogspot.com/2009/02/testing-datetimenow-introducing-wrap.html

  • http://codebetter.com/blogs/jeremy.miller Jeremy D. Miller

    @Martin,

    As if I’m really that smart and observant at 5 in the morning;)

  • Martin Watts

    @Jeremy,

    It was a joke ;) I thought the “:P” would give it away. If I ever saw anybody here setting the clock for such reasons I would personaly kick them out of the window.

    I like the “TODAY-#” approach. However, from a readability stand point, I would wonder what the -2 in “TODAY-2″ actually means. It could also be weeks, months or whatever. Especially since I would also like a NOW, and “NOW-4″ could mean even more things.

    I think “NOW-2h”, or “NOW-04:33:10.123″ would be nice.

  • http://darrell.mozingo.net Darrell Mozingo

    We’ve been using Oren’s implementation for a while now and its worked great:

    http://ayende.com/Blog/archive/2008/07/07/Dealing-with-time-in-tests.aspx

  • http://codebetter.com/blogs/jeremy.miller Jeremy D. Miller

    @Kent,

    It would I believe, but as in almost all proposed uses of TypeMock I’d have to ask if it actually was a good idea to retreat to TypeMock.

    You’d still have the test readability issue with that.

  • http://kentb.blogspot.com Kent Boogaart

    Good post Jeremy.

    Isn’t there a third option here? I haven’t used it, but won’t TypeMock allow you to intercept the DateTime.Now call and return whatever data you want? Just curious.

  • Gerry Lowe

    I don’t agree that you’re stuck with problem (b) if you adopt approach #1.

    The hard-wired date value used by the dummy clock for “today” is (should be!) available for testers to write something fairly readable so that they don’t have to unreadably hard-wire a particular date that’s “known” to be 7 days from “now”. But never hard-wired dates that require the reader to parse.

    So in a C# nunit test class, you might see something like “MyTest.Today.AddDays(7)”; or in xml config you might see something like your StoryTeller example.

  • http://codebetter.com/blogs/jeremy.miller Jeremy D. Miller

    @Martin,

    You might want to reread the part where I talk about the readability of the test data and how important that is.

  • http://blog.fohjin.com Mark Nijhof

    Hmmm I haven’t looked at StoryTeller yet, so I don’t know for sure. But I do remember someone shouting out something about “Is XML dead yet”, but by the looks of it I would say no :)

    Good read other wise.

    -Mark

  • Martin Watts

    There is another option:

    3. set the system clock to a fixed date before running your tests. It’s only one api call away… :P

  • http://www.jameskovacs.com james.kovacs

    Sometimes you need IClock because relative dates/times won’t do. I’ve worked on systems where you can only enter new invoices during normal business hours (unless you’re a manager); or you receive a discount on weekends and holidays; or some other business logic dependent on particular dates/times rather than relative. You then need IClock to make the system reliably testable. Otherwise you’ll have spurious test failures if you happen to be working late.

  • Anne Wax

    Thank you, Jeremy. I am a new .Net developer and this example is directly relevant to work I am doing.