Search code examples
c#unit-testingdatetimemstestsystemtime

Unit Testing: DateTime.Now


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?


Solution

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