How to mock non-mockables (such as DateTime.Now) in unit tests using Ambient Context Pattern

Note: There is a sample code for this article on GitHub which could be accessed from the following link : https://github.com/akazemis/TestableDateTimeProvider

One of the common dependencies that are sort of hard to test are methods that use DateTime.Now or DateTime.UtcNow in their body. Take this method for instance:

public bool IsThereWorldCupThisYear()
{
      var currentYear = DateTime.Now.Year;
      return ((currentYear - 1998) % 4) == 0; 
}

As you see, this method gets the current year and checks if there is any soccer world cup this year. Now suppose you want to write unit test for it. How would you do that? How would you test if it works correctly in 2018, 2022 and so forth. Would you change current system date and run your test? Not a good idea obviously!

In this method you have a dependency and that’s the static variable of DateTime.Now which returns the current system time. To be able to test this method you need to mock up that dependency in your tests. One way of doing that is using Shims  in dotnet (which is actually unconstrained isolation). There’s some problems with that though. Firstly, at the time of writing this article, it’s not supported by dotnet core. Secondly, it’s slow and thirdly, I don’t like that fake assembly referencing thing.

The cleaner way of doing that, if you’re using dependency injection in your project, is to create an interface and a class, wrapped around the DateTime.Now, injecting that helper wherever we need to get current system date in production code and mock it up in unit tests. Something like this:

 public interface IDateTimeHelper
 {
     DateTime GetDateTimeNow();
 }

 public class DateTimeHelper : IDateTimeHelper
 {
     public DateTime GetDateTimeNow()
     {
         return DateTime.Now;
     }
 }
 public class WorldCupHandler
 {
     private IDateTimeHelper _dateTimeHelper;
     public WorldCupHandler(IDateTimeHelper dateTimeHelper)
     {
        _dateTimeHelper = dateTimeHelper;
     }

     public bool IsThereWorldCupThisYear()
     {
         var currentYear = _dateTimeHelper.GetDateTimeNow().Year;
         return ((currentYear - 1998) % 4) == 0;
     }
 }

Then wherever you want to write unit test for the classes injecting IDateTimeHelper, you can easily mock it up. In our case, a unit test for WorldCupHandler would look like this (here I’m using xUnit.netMoq , and FluentAssertions in examples):

public class WorldCupHandlerTest()
{
    [Fact]
    public void IsThereWorldCupThisYear_WhenWorldCupYear_ReturnsTrue()
    {
       var mockDateTimeHelper = new Mock<IDateTimeHelper>();
       var fakeDate = new DateTime(2018, 05, 15);
       mockDateTimeHelper.Setup(o => o.GetDateTimeNow()).Returns(fakeDate);
       var worldCupHandler = new WorldCupHandler(mockDateTimeHelper.Object);
       
       var result = worldCupHandler.IsThereWorldCupThisYear();
       
       result.Should().Be(true);
    }

    [Fact]
    public void IsThereWorldCupThisYear_WhenNonWorldCupYear_ReturnsFalse()
    {
       var mockDateTimeHelper = new Mock<IDateTimeHelper>();
       var fakeDate = new DateTime(2020, 07, 10);
       mockDateTimeHelper.Setup(o => o.GetDateTimeNow()).Returns(fakeDate);
       var worldCupHandler = new WorldCupHandler(mockDateTimeHelper.Object);
 
       var result = worldCupHandler.IsThereWorldCupThisYear();

       result.Should().Be(false);
    }
}

It’s testable and clean and there is nothing wrong with it as long as you’re using dependency injection, and you have no problem with adding IDateTimeHelper as a dependency to your projects. Some people don’t like to add such a simple class as a dependency wherever they need system’s current date and time. As it sometimes happen to be a vast majority of classes in the project.

Additionally, what if you have a legacy code and you just want to refactor the code to replace the scattered DateTime.Now or DateTime.UtcNow in the code with your helper method to have more control over it and also make your code testable? If you’ve done such a thing before you would know how it hurts! You may end up with a dependency domino which affects most of your classes. You may end up  injecting IDateTimeHelper into almost every class in your code! That’s the case even for non-legacy code. Nonetheless you’re doing your project from scratch and too many modifications is not the problem, you may end up adding IDateTimeHelper as injected dependency everywhere, which doesn’t seem to be clean.

In this article, I’m going to suggest a clean approach to do so (at least I think it’s cleaner!). And that’s using Ambient Context Pattern and ThreadLocal class. Don’t fear the buzz words it’s not rocket science whatsoever.

Here’s the thing, what we need to do is just creating a helper class (in hear I’ve named it DateTimeProvider and made it a singleton), using that DateTimeProvider in our production code in a quite simple way, and simply wrap it in a context object once we need to fake it. Simple as that!

Here’s how we’d use it in our production code:


 public class WorldCupHandler
 {
     private IDateTimeHelper _dateTimeHelper;
     public WorldCupHandler(IDateTimeHelper dateTimeHelper)
     {
         _dateTimeHelper = dateTimeHelper;
     }

     public bool IsThereWorldCupThisYear()
     {
        var currentYear = DateTimeProvider.Instance.GetUtcNow().Year;
        return ((currentYear - 1998) % 4) == 0;
     }
 }

And here’s how we’d write our unit tests:

public class WorldCupHandlerTest
{
    [Fact]
    public void IsThereWorldCupThisYear_WhenWorldCupYear_ReturnsTrue()
    {
       var fakeDate = new DateTime(2018, 05, 15);
       DateTime result = default(DateTime);
       var worldCupHandler = new WorldCupHandler();

       using(var context = new DateTimeProviderContext(fakeDate))
       {
          result = worldCupHandler.IsThereWorldCupThisYear();
       }
       result.Should().Be(true);
    }

    [Fact]
    public void IsThereWorldCupThisYear_WhenNonWorldCupYear_ReturnsFalse()
    {
       var fakeDate = new DateTime(2020, 07, 30);
       var worldCupHandler = new WorldCupHandler();

       using(var context = new DateTimeProviderContext(fakeDate))
       {
           var result = worldCupHandler.IsThereWorldCupThisYear();
       }

       result.Should().Be(false);
    }
}

As you see in the code above, the only thing we need to do to fake the current system date as an arbitrary date is to wrap our method call in a “using” block that creates a new DateTimeProviderContext instance and set the date as it’s constructor’s argument. That’s it!

Now let’s have a look into the code and see how this magic works. Here’s our DateTimeProvider code:

public class DateTimeProvider
 {
    #region Singleton
    private static Lazy<DateTimeProvider> _lazyInstance = new Lazy<DateTimeProvider>(() => new DateTimeProvider());
    private DateTimeProvider()
    {
    }
    public static DateTimeProvider Instance
    {
        get
        {
           return _lazyInstance.Value;
        }
    }
    #endregion

    private Func<DateTime> _defaultCurrentFunction = () => DateTime.UtcNow;

    public DateTime GetUtcNow()
    {
      if (DateTimeProviderContext.Current == null)
      {
         return _defaultCurrentFunction.Invoke();
      }
      else
      {
         return DateTimeProviderContext.Current.ContextDateTimeUtcNow;
      }
   }
 }

Notice the “GetUtcNow()” method, we’re using a Static property named DateTimeProviderContext.Current to check if our method is being wrapped in a context or not. If it’s not wrapped, we would return the result of _defaultCurrentFunction which is a function delegate that returns the system current datetime. Otherwise it gets the DateTime from the context that wraps around our method call.

Let’s see how our DateTimeProviderContext looks like:

 public class DateTimeProviderContext : IDisposable
 {
    private static ThreadLocal<Stack> ThreadScopeStack = new ThreadLocal<Stack>(() => new Stack());
    public DateTime ContextDateTimeUtcNow;
    private Stack _contextStack = new Stack();

    public DateTimeProviderContext(DateTime contextDateTimeUtcNow)
    {
       ContextDateTimeUtcNow = contextDateTimeUtcNow;
       ThreadScopeStack.Value.Push(this);
    }
    public static DateTimeProviderContext Current
    {
       get
       {
          if (ThreadScopeStack.Value.Count == 0)
          {
             return null;
          }
          return ThreadScopeStack.Value.Peek();
       }
   }

    #region IDisposable Members
    public void Dispose()
    {
       ThreadScopeStack.Value.Pop();
    }
    #endregion
 }

And here’s where the magic happens. In the DateTimeProviderContext we’ve used Ambient Context Pattern in conjunction with ThreadLocal class to facilitate faking current system date and time.

First of all, notice that it implements IDisposable interface. So once we create a using block and wrap it around our code, it would create the context object at the beginning of the block and calls the Dispose() method at the end of the block. Therefore we can create the instance with a property of the fake date, which is being passed as the constructor’s argument, and add the context object to a stack of contexts. Then once the using block is closed, Dispose methods gets called and we would pop the context off the stack. And at any time we get the DateTimeProviderContext.Current, it would return the most inner context wrapped around our code.

Using stack we can even have nested context blocks in our test code if we need to do so. Like the following code :

 [Fact]
 public void GetUtcNow_WhenMultipleContext_ReturnsCorrectFakeUtcNow()
 {
     var fakeDate1 = new DateTime(2018, 5, 26, 10, 0, 0, DateTimeKind.Utc);
     var fakeDate2 = new DateTime(2020, 7, 15, 12, 30, 0, DateTimeKind.Utc);
     DateTime result1;
     DateTime result2;

     using (var context1 = new DateTimeProviderContext(fakeDate1))
     {
         result1 = DateTimeProvider.Instance.GetUtcNow();
         using (var context2 = new DateTimeProviderContext(fakeDate2))
         {
            result2 = DateTimeProvider.Instance.GetUtcNow();
         }
     }

     result1.Should().Be(fakeDate1);
     result2.Should().Be(fakeDate2);
 }

Why did we use  ThreadLocal<Stack> to keep our stack in? Because if we run our tests in parallel, they’re gonna run in separate threads and as we’re using a static field to hold the stack in, they would interfere with each other and cause intermittent errors. Using ThreadLocal would make that static field (ThreadScopeStack) thread-safe.

That’s it! Using this cool trick you can skip a lot of effort refactoring your code and make it testable. It took me a while to come up with this solution, hopefully you’d save much time reading this article.

The only downside of this approach is that we actually modified the production code’s logic to support unit testing scenarios which is not a good practice but I’d say that’s not gonna be a big problem in this specific case. As it does the job by minimum amount of modification in legacy code.

Not to mention, I’ve taken DateTime.Now or DateTime.UtcNow as an example of testing non-testable stuff , using the same technique you can handle similar scenarios in your code.

By the way, I have also made a repository on GitHub website and pushed the sample code in it, you can see the code in action in there.

Good Luck!

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