Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unit Testing: DateTime.Now

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 replace 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().


These are all good answers, this is what I did on a different project:

Usage:

Get Today's REAL date Time

var today = SystemTime.Now().Date;

Instead of using DateTime.Now, you need to use SystemTime.Now()... It's not hard change but this solution might not be ideal for all projects.

Time Traveling (Lets go 5 years in the future)

SystemTime.SetDateTime(today.AddYears(5));

Get Our Fake "today" (will be 5 years from 'today')

var fakeToday = SystemTime.Now().Date;

Reset the date

SystemTime.ResetDateTime();

/// <summary>
/// Used for getting DateTime.Now(), time is changeable for unit testing
/// </summary>
public static class SystemTime
{
    /// <summary> Normally this is a pass-through to DateTime.Now, but it can be overridden with SetDateTime( .. ) for testing or debugging.
    /// </summary>
    public static Func<DateTime> Now = () => DateTime.Now;

    /// <summary> Set time to return when SystemTime.Now() is called.
    /// </summary>
    public static void SetDateTime(DateTime dateTimeNow)
    {
        Now = () =>  dateTimeNow;
    }

    /// <summary> Resets SystemTime.Now() to return DateTime.Now.
    /// </summary>
    public static void ResetDateTime()
    {
        Now = () => DateTime.Now;
    }
}

Moles:

[Test]  
public void TestOfDateTime()  
{  
      var firstValue = DateTime.Now;
      MDateTime.NowGet = () => new DateTime(2000,1,1);
      var secondValue = DateTime.Now;
      Assert(firstValue > secondValue); // would be false if 'moleing' failed
}

Disclaimer - I work on Moles


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 change 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