Mayo
Mayo

Reputation: 10812

Strategies for dealing with DateTime.Now in unit tests

I have business logic that does not perform certain functions on Saturday or Sunday. I want my unit tests to verify that these functions are performed, but the tests will fail on Saturday/Sunday.

I figure the easiest route is to have the unit tests convey a friendly message stating that the test results are not valid if they are run on Saturday/Sunday.

With C# / NUnit...

Assert.That(
  DateTime.Now.DayOfWeek != DayOfWeek.Sunday && DateTime.Now.DayOfWeek !=      
  DayOfWeek.Saturday, "This test cannot be run on Saturday or Sunday");

Is it possible/advisable to try and mock the date? Are there other strategies for handling this scenario?

Upvotes: 8

Views: 9420

Answers (3)

Steven
Steven

Reputation: 172865

You should create an abstraction over the system clock, as the system clock represents a Volatile Dependency. This allows you write RTM unit tests on code that depends on system resources and external resources.

An abstraction over the system clock is pretty simple and could look like this:

public interface ISystemClock
{
    DateTime Now { get; }
}

Classes in your application that depend on the system clock should have an ISystemClock as constructor argument. For unit testing scenario's you can use an ISystemClock implementation that looks like this:

public class FakeSystemClock : ISystemClock
{
    // Note the setter.
    public DateTime Now { get; set; }
}

Now you can use this fake clock in your unit tests like this:

[TestMethod]
[ExpectedException(typeof(InvalidOperationException),
    "Service should throw an exception when called on saturday.")]
public void DoSomething_WhenCalledOnSaturday_ThrowsException()
{
    // Arrange
    var saturday = new DateTime(2011, 1, 1);
    Assert.AreEqual(saturday.DayOfWeek, DayOfWeek.Saturday,
        "Test setup fail");
    var clock = new FakeSystemClock() { Now = saturday };
    var service = new Service(clock);

    // Act
    service.DoSomething();
}

In your application project you can define an ISystemClock implementation that actually uses the system clock. Using the given ISystemClock definition, it will look like this:

public class RealSystemClock : ISystemClock
{
    public DateTime Now => DateTime.Now;
}

When you use a DI container, you can configure it to wire up your types and automatically inject instances of RealSystemClock when a constructor specifies a ISystemClock argument.

btw. While you could use Func<DateTime> delegates to do the trick, defining an interface for this is IMO much more explicit, less ambiguous, and more readable to other developers. Besides that, wiring everything in a DI container would be easier, because it's easy to end up with multiple Func<DateTime> dependencies that all do different things than "give me the current local time".

Upvotes: 8

Sevenate
Sevenate

Reputation: 6495

Noda Time might be a solution/alternative for new projects as it all interface based and, thus, easily mock-able.

Upvotes: -1

Darin Dimitrov
Darin Dimitrov

Reputation: 1039528

Is it possible/advisable to try and mock the date?

Yes, not only that it is advisable but it is the only way to reliably unit test your code in isolation. Suppose that you wanted to test the following (meaningless) method:

public bool Foo()
{
    return (DateTime.Now.DayOfWeek == DayOfWeek.Sunday);
}

It's more than obvious that you can't test it because it relies on a static method (the static Now property to be more precise). It's clear that components which are tightly coupled like this cannot be unit tested in isolation.

Now consider this improvement (separation of concerns with constructor DI):

private readonly Func<DateTime> _nowProvider;
public SomeClass(Func<DateTime> nowProvider)
{
    _nowProvider = nowProvider;
}

public bool Foo()
{
    return (_nowProvider().DayOfWeek == DayOfWeek.Sunday);
}

Now that's much better and easier to unit test. SomeClass no longer depends on a non-deterministic date. In the real application you would obviously instantiate SomeClass like this:

var s = new SomeClass(() => DateTime.Now);
s.Foo();

and in your unit test you will mock it so that you can verify both cases:

var subjectUnderTest = new SomeClass(() => new DateTime(2011, 1, 3));
var actual = subjectUnderTest.Foo();
// assertions, ...

Upvotes: 13

Related Questions