Testing Against The Current Time

This is the third in a small series of posts about testing against non-determinism. In this installation, I'm going to cover how to deal with the current time or date.

If you have logic that is dependent of the current time or date, test results will vary according to the time you run your tests. As always, my examples tend toward the overly simplistic, but consider this method:

 public bool IsTodayAWeekDay()
 {
     DateTime now = DateTime.Now;
  
     if ((now.DayOfWeek == DayOfWeek.Saturday) ||
         (now.DayOfWeek == DayOfWeek.Sunday))
     {
         return false;
     }
  
     return true;
 }

The main point of this example is that the code braches on the value of DateTime.Now. Obviously, if you execute a test against this method on a weekday, the method will return true, but otherwise it will return false.

 [TestMethod]
 public void UseNonDeterministicTimeConsumer()
 {
     NonDeterministicTimeConsumer tc = new NonDeterministicTimeConsumer();
  
     bool result = tc.IsTodayAWeekDay();
  
     // How to verify result?
 }

As you can see, you can't really test this method. A quick fix for this could be to change the method's signature to accept the current time as a parameter, but this may not be a good idea for a number of reasons (which I'll leave as an exercise for the interested reader).

If changing the method's signature is not an option, then how can you test such a method?

This is a case where the Provider Injection pattern really comes into its own. Unlike other Inversion of Control patterns, Provider Injection can delay the creation of a dependency until it is needed. Since the current time is continually changing, you should never request an instance before you need it, and that's exactly what you can do with Provider Injection.

As always, I'm using Service Locator 2's ServiceProvider<T>, but you can always use an implementation of IServiceProvider and do a bit of casting if you want to stick to the BCL proper.

 public partial class TimeConsumer
{
    private ServiceProvider<DateTime> dateTimeProvider_;
 
    public TimeConsumer(ServiceProvider<DateTime> dateTimeProvider)
    {
        this.dateTimeProvider_ = dateTimeProvider;
    }
 
    public bool IsTodayAWeekDay()
    {
        DateTime now = this.dateTimeProvider_.Create("Now");
 
        if ((now.DayOfWeek == DayOfWeek.Saturday) ||
            (now.DayOfWeek == DayOfWeek.Sunday))
        {
            return false;
        }
 
        return true;
    }
}

The trick is to defer creation of the DateTime instance until you need it. Incidentally, you see here a rather nice feature of Service Locator 2 illustrated: Requesting a DateTime instance named "Now" allows Service Locator to distinguish between several requested DateTime objects (you might also want to be able to create what corresponds to DateTime.Today), but it also allows Service Locator to infer the correct creation strategy for the requested instance. This means that unless you intercept it by presetting or configuring a value, ServiceProvider<DateTime> will simply use DateTime.Now.

In the test, however, you do want to intercept the default strategy by presetting the current time to a predefined value:

 [TestMethod]
 public void CheckMonday()
 {
     ServiceProvider<DateTime> dateTimeProvider = 
         new ServiceProvider<DateTime>();
     // 2007-05-07 is a Monday
     dateTimeProvider.Preset(new DateTime(2007, 5, 7), "Now");
  
     TimeConsumer tc = new TimeConsumer(dateTimeProvider);
  
     Assert.IsTrue(tc.IsTodayAWeekDay());
 }

When executed, the Create method will always return the preset instance - in this case alway May 7th, 2007, which is a Monday. Obviously, you can write similar tests for the other days of the week.

Next: Testing Against The Passage of Time