Moq and Lazy Loading

I’ve just completed a bit of TDD. Proper TDD, I wrote the code to pass the tests and wrote the tests first. Anyway, one thing I needed control over was the concept of “now”. This particular method I was testing/writing deals with DateTime objects.

I needed a way of defining, on a per test basis, what “now” is. There were two steps to achieving this. Firstly, I needed to use an interface to get “now”.

public interface IDateTimeProvider
{
    DateTime Now { get; }
}

So, rather than using DateTime.Now, I use DateTimeProvider.Now.

The second step is creating the mock IDateTimeProvider and using the lazy loading feature of Moq. In this particular case I am using Moq and a test base class …

[TestFixture]
public abstract class BaseTest
{
    private Mock<IDateTimeProvider> _mockDateTimeProvider;
    protected IDateTimeProvider DateTimeProvider { get { return _mockDateTimeProvider.Object; } }

    [SetUp]
    public void Setup()
    {
        _mockDateTimeProvider = new Mock<IDateTimeProvider>();
        Now = DateTime.MinValue;
        _mockDateTimeProvider.Setup(x => x.Now).Returns(() =>
        {
            if (Now == DateTime.MinValue)
            {
                throw new Exception("'Now' property must be set in each [Test] method");
            }
            return Now;
        });
    }

    protected DateTime Now { private get; set; }
}

So, I have a DateTime property, which is evaluated whenever the IDateTimeProvider.Now method is called. I added a little check to make sure the Now property is set in each test. That, coupled with setting Now to DateTime.MinValue means any test which doesn’t set the value will get a helpful message in your chosen test runner.

The crucial part of that code and what makes it really useful is the lazy evaluation, which is done like so …

() =>
{
    if (Now == DateTime.MinValue)
    {
        throw new Exception("'Now' property must be set in each [Test] method");
    }
    return Now;
}

I’ve also made the Now and _mockDateTimeProvider private so they cannot be used, forcing the use of the DateTimeProvider.

Here is an example test …

[Test]
public void ExampleTest()
{
    Now = DateTime.Parse("30 Nov 2011 15:00");

    // Arrange

    // Act
    var result = DateTimeProvider.Now;

    // Assert
    Assert.That(result, Is.EqualTo(DateTime.Parse("30 Nov 2011 15:00")));
}

I’ve explicitly defined the dates in my asserts so it’s a little more obvious what is going on. I’ve also used DateTime.Parse as it’s easier to read. I’ve put the assignment of the Now property before the Arrange section as it’s really a pre-arrange step.

It’s not totally necessary to require Now to be configured explicitly for each test, but I think it makes things a little easier to read for developers coming to this code fresh. It would be possible to have a default value for “Now” but that isn’t very discoverable.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s