New to Testing Time-Dependent Code?... Use NodaTime.Testing!

When you’re just getting into testing and your app has a lot of time-dependent logic - you might feel like you’ve picked the wrong app to start testing with.

You could either:

For example, say you have a lot of classes that you want to stamp with a DateTime CreatedOn {get; set;} property

To set this property, you assign it with DateTime.Now in the constructor:

public class MyClass {
    public IStock Stock { get; private set; }
    public IMarketDay MarketDay { get; private set; }
    public DateTime CreatedOn { get; }

    public MyClass(IStock stock, IMarketDay marketDay) {
        Stock = stock;
        MarketDay = marketDay;
        CreatedOn = DateTime.Now;
    }
}

As of now, you don’t have much to test… but what if some behavior depended on the CreatedOn property? Maybe you have some branching logic:

public class MyClass {
    public IStock Stock { get; private set; }
    public IMarketDay MarketDay { get; private set; }
    public DateTime CreatedOn { get; }
    public string thingState { get; private set; }

    public MyClass(IStock stock, IMarketDay marketDay) {
        Stock = stock;
        MarketDay = marketDay;
        CreatedOn = DateTime.Now;

        if (marketDay.Date.ToDateTimeUtc() == CreatedOn.Date) {
            DoThing1();
        }
        else {
            DoThing2();
        }
    }
    private void DoThing1() {
        thingState = "Thing1 done!";
    }
    private void DoThing2() {
        thingState = "Thing2 done!";
    }
}

So how would we test this branching logic? Ideally, we want to find a way to decouple our class from its dependency on System.DateTime.Now

NodaTime’s FakeClock

There are ways to decouple System.DateTime.Now without adding 3rd-party references (using a generic func delegate like here and here).

However… DateTime is flawed, so I prefer to use NodaTime because it makes me think about my unique concept of Time in my application.

NodaTime has a SystemClock of its own. It exists as a Singleton that implements an IClock interface. Conveniently, NodaTime.Testing offers FakeClock for our testing needs. Here’s our new class with NodaTime instead of DateTime:

public class MyClassNodaTime {
    public IStock Stock { get; private set; }
    public IMarketDay MarketDay { get; private set; }
    public Instant CreatedOn { get; private set; }
    public string thingState { get; private set; }

    public MyClassNodaTime(
            IStock stock,
            IMarketDay marketDay,
            IClock clock) {
        Stock = stock;
        MarketDay = marketDay;
        CreatedOn = clock.Now;

        if (marketDay.Date.InUtc().Date == CreatedOn.InUtc().Date) {
            DoThing1();
        }
        else {
            DoThing2();
        }
    }

    private void DoThing1() {
        thingState = "Thing1 done!";
    }
    private void DoThing2() {
        thingState = "Thing2 done!";
    }
}

You can see that we are injecting any object that implements IClock into our class. We are also using NodaTime Instant now, which is not time-zone aware (because that’s not a requirement… yet).

Here’s a test of our branching logic which shouldn’t be in the class constructor:

[Fact]
public void when_on_a_market_day_do_thing1() {
    var clock = new FakeClock(
        Instant.FromUtc(2016, 5, 8, 0, 0));
    var stock = new Stock();
    var marketDay = new MarketDay(
        Instant.FromUtc(2016, 5, 8, 0, 0));

    var myClass = new MyClassNodaTime(stock, marketDay, clock);

    Assert.Equal("Thing1 done!", myClass.thingState);
}

[Fact]
public void when_not_on_a_market_day_do_thing2() {
    var clock = new FakeClock(
        Instant.FromUtc(2016, 5, 8, 0, 0));
    var stock = new Stock();
    var marketDay = new MarketDay(
        Instant.FromUtc(2016, 5, 8, 0, 0));

    var myClass = new MyClassNodaTime(stock, marketDay, clock);

    Assert.Equal("Thing2 done!", myClass.thingState);
}

Super Power

By the way, I snuck in a super power for you if you didn’t pick that up yet… We injected the dependency for IClock… we are doing Dependency Injection!!!

This does wonders for the testability of your app. Without all the complexity of IoC Containers, etc. it’s actually easy to start using on your own.

Summary

NodaTime.Testing has a really handy implementation of its IClock called FakeClock that allows you to control the dependency of your app on SystemClock.

NodaTime is the way to go when you’re doing complex stuff with Dates and Times because Dates and Times can be more complex than System.DateTime leads you to believe.

Hope this helps you one day! Thanks for reading and be sure to sign up for my newsletter so you can get these tips sent right to your inbox!


Related Posts:


Sources:

Tweet
comments powered by Disqus