views:

408

answers:

4

I have some unit tests that expects the 'current time' to be different than DateTime.Now and I don't want to change the computer's time, obviously. What's the best strategy to achieve this?

Thanks

+1  A: 

Mock Objects.

A mock DateTime that returns a Now that's appropriate for your test.

S.Lott
+7  A: 

The best strategy is to wrap the current time in an abstraction and inject that abstraction into the consumer.

Alternatively, you can also define a time abstraction as an Ambient Context:

public abstract class TimeProvider
{
    private static TimeProvider current =
        DefaultTimeProvider.Instance;

    public static TimeProvider Current
    {
       get { return TimeProvider.current; }
       set 
       {
           if (value == null)
           {
               throw new ArgumentNullException("value");
           }
           TimeProvider.current = value; 
       }
   }

   public abstract DateTime UtcNow { get; }

   public static void ResetToDefault()
   {    
       TimeProvider.current = DefaultTimeProvider.Instance;
   }            
}

This will enable you to consume it like this:

var now = TimeProvider.Current.UtcNow;

In a unit test, you can replate TimeProvider.Current with a Test Double/Mock object. Example using Moq:

var timeMock = new Mock<TimeProvider>();
timeMock.SetupGet(tp => tp.UtcNow).Returns(new DateTime(2010, 3, 11));
TimeProvider.Current = timeMock.Object;

However, when unit testing with static state, always remember to tear down your fixture by calling TimeProvider.ResetToDefault().

Mark Seemann
Thank you, this enables extreme flexibility and allows me to also control time in other situations than unit testing (since in unit testing mocking would be enough).
Pedro
+2  A: 

You have some options for doing it:

  1. Use mocking framework and use a DateTimeService (Implement a small wrapper class and inject it to production code). The wrapper implementation will access DateTime and in the tests you'll be able to mock the wrapper class.

  2. Use Typemock Isolator, it can fake DateTime.Now and won't require you to change the code under test.

  3. Use Moles, it can also fake DateTime.Now and won't require chance in production code.

Some examples:

Wrapper class using Moq:

[Test]
public void TestOfDateTime()
{
     var mock = new Mock<IDateTime>();
     mock.Setup(fake => fake.Now)
         .Returns(new DateTime(2000, 1, 1));

     var result = new UnderTest(mock.Object).CalculateSomethingBasedOnDate();
}

public class DateTimeWrapper : IDateTime
{
      public DateTime Now { get { return DateTime.Now; } }
}

Faking DateTime directly using Isolator:

[Test]
public void TestOfDateTime()
{
     Isolate.WhenCalled(() => DateTime.Now).WillReturn(new DateTime(2000, 1, 1));

     var result = new UnderTest().CalculateSomethingBasedOnDate();
}

Disclaimer - I work at Typemock

Elisha
A: 

Moles:

[Test]  
public void TestOfDateTime()  
{  
    MDateTime.NowGet = () => new DateTime(2000,1,1);

    var result = new UnderTest().CalculateSomethingBasedOnDate();  
}

Disclaimer - I work on Moles

Peli