Obstacles of unit testing & methods to side step them – Part 1: date and time

Introduction

Unit testing is good. Sure it’s no silver bullet but there is no such thing! Looking back I’m not sure how I managed to work without writing unit tests the number of time having tests has saved me from tracking down bugs isn’t worth thinking about 🙂

However everything isn’t all rosy once you start unit testing against your code, there are a number of items that can put a spanner in the works, and that’s why I have created this series of posts to try and help other people resolve some of these common issues you may find yourself up against.

  • Date & Time
  • Multi-threading
  • External Dependencies
  • Sharing Static items

Prerequisites

  • You have already been writing unit tests
  • You are familar with state based testing and behaviour based testing & when best to use either
  • Are familar with a mocking framework (these examples use Rhino Mocks).

Date & Time

This first post covers testing against date and time, it is quite common to want to use the current date/time in your objects, say for example you have an audit object that keeps track of any changes made to a user of a system by an admin, it’s not unreasonable to expect a method along the way performing something like this:

public void PasswordChanged(string newPassword, IAdministrator changedBy)
{
    //.. set audit values
    passwordAudit.ChangedAt = DateTime.Now;
    auditService.Save(passwordAudit);
}

Now we have a problem how can we test that the audit service object got called with the correct values? Lets write a test for this:

[TestFixture]
public class when_changing_password_for_user : ContextSpecification
{
 //.. [snip]
    [Test]
    public void should_record_correct_values_against_audit_record()
    {
        mockAuditService.Expect(x => x.Save(null), o => o.IgnoreArguments())
                .Do(new Action(delegate(IAuditRecord audit))
                {
                    changedby.Username.ShouldEqual(audit.ChangedbyUsername);
                    audit.ChangedAt.ShouldEqual(DateTime.Now); // this aint gonna work!
                }
    }

    IPasswordAudit sut = createSUT();
    sut.PasswordChanged(newPwd, changedBy);
    }
 //..[snip]
}

I provided the textfixture to give some context to the testing taking place, this is the style I choose to write my tests it is based around BDD (Behaviour Driven Design).

As we can see audit.ChangedAt.ShouldEqual(DateTime.Now)); this bit of code is not going to do the trick, given that stopping time is out of the question, we have some other options a simpler way and one that works in this scenario is to push the date & time into the method call:

public void PasswordChanged(string newPassword, DateTime changedAt, IAdministrator changedBy)
{
    //.. set audit values
    passwordAudit.ChangedAt = changedAt;
    auditService.Save(passwordAudit);
}

We could now update the unit test to take this into account and check the DateTime values match (i’m not going to show this), there’s a lot to be said about KISS however sometimes this push approach can simple not be applied.

Take for example a scheduler object that is tracking the current date & time the push approach is unworkable, we need to some way to provide the scheduler object with a DateTime value but for it to be under our control.

There are 2 different styles that I know of to get date and time under our control, the first is by using an abstraction type that provides us with the current date and time:

public interface ICurrentDateTimeProvider
{
    DateTime Current {get;}
}

We can then use in our class that needs to access the current date and time, this would be a good argument for using property injection as it’s only a seam for testing purposes:

public class Scheduler
{
    protected ICurrentDateTimeProvider currentDateTimeProvider = new RealDateTimeProvider();

    public ICurrentDateTimeProvider DateTimeProvider
    {
        set {currentDateTimeProvider = value;}
    }
}

RealDateTimeProvider simply returns DateTime.Now.

We can now use this abstraction to control date and time:

[TextFixture]
public void when_scheduler_is_started : ContextSpecification
{
    [Test]
    public void given_schedule_item_next_run_is_in_the_past_should_execute_it()
    {
        currentDateTimeProvider.Stub(x => x.Current).Return(scheduleItemNextRun.AddMinutes(-1));
        var sut = createSUT();
        sut.CurrentDateTimeProvider = currentDateTimeProvider;
        sut.Start();
        //...[SNIP]
    }
}

I have kept the code to a minimum to show the important part where we stub out the ICurrentDateTimeProvider and inject it via the property on Scheduler.

The problem with this approach is that it’s a little long-winded, we have to introduce a new interface, create the real implementation and also find a way to inject the dependency into the class were testing against.

There is a really nice way I came across recently that gets round these issues it was while reading ayende’s blog, the code needed is incredibly small but gives us a lot:

public static class SystemTime
{
    public static Func Now = () => DateTime.Now;
}

This gives us one place to go to for date and time so no injection needed, it also provides us with the real date and time and allows us to control what should be returned. Code wanting to get the current date and time would use this:

var currentDateTime = SystemTime.Now();

Code wanting to change what is returned i.e. out test code would do so like this:

SystemTime.Now = () => new DateTime(2009,1,1);

Very neat and requires less work than the former way. In the next post we will look out dealing with testing multi-threading code.

Advertisements

One thought on “Obstacles of unit testing & methods to side step them – Part 1: date and time

  1. Pingback: Obstacles of unit testing & methods to side step them – Part 2: multi-threading | Journal of a software dev

Comments are closed.