English
Français

Blog of Denis VOITURON

for a better .NET world

How to mock DateTime in Unit Tests (C#)

Posted on 2020-01-22

Today, a developer came to me asking how to test his code that contains a reference to DateTime.Now. Indeed, sometimes your application treats its data differently, depending on today’s date. For example, how do you check the following code, which depends on the current trimester?

int trimester = (DateTime.Today.Month - 1) / 3 + 1;
if (trimester <= 2)
    ...
else 
    ...

Dependency injection

A clean way to proceed, if you use the Dependency Injection (IoC) in your project, is to create an interface to be injected wherever you want to get the current system date. And to define it, according to your needs, in unit tests.

public interface IDateTimeHelper
{
    DateTime GetDateTimeNow();
}

public class DateTimeHelper : IDateTimeHelper
{
    public DateTime GetDateTimeNow()
    {
        return DateTime.Now;
    }
}

It works fine, as long as you use the addiction injection. But some people don’t like to inject such a simple class. Also, what happens if you have an existing code and you just want to rewrite it to replace the date and time?

Ambient Context Model

In order to avoid injecting a so simple class as giving the date and time; And to simplify the updating of existing code, I propose a solution that uses the ** Ambient Context Model**.

To do this, we simply need to use a DateTimeProvider class that determines the current usage context: DateTime.Now is replaced by DateTimeProvider.Now.

int trimester = (DateTimeProvider.Today.Month - 1) / 3 + 1;

This Provider returns the current system date. However, by using it in a unit test, we can adapt the context for specify a predefined date.

var result = DateTimeProvider.Now;      // Returns DateTime.Now

var fakeDate = new DateTime(2018, 5, 26);
using (var context = new DateTimeProviderContext(fakeDate))
{
    var result = DateTimeProvider.Now;  // Returns 2018-05-26
}

As you can see in the code above, the only thing we have to do to simulate the current system date, is to wrap our method call in a using block. This creates a new instance of DateTimeProviderContext and specifies the desired date as the constructor’s argument. That’s it!

Code source

The class DateTimeProvider is:

public class DateTimeProvider
{
    public static DateTime Now
        => DateTimeProviderContext.Current == null
                ? DateTime.Now
                : DateTimeProviderContext.Current.ContextDateTimeNow;

    public static DateTime UtcNow => Now.ToUniversalTime();

    public static DateTime Today => Now.Date;
}

And the class DateTimeProviderContext is:

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

    public DateTimeProviderContext(DateTime contextDateTimeNow)
    {
        ContextDateTimeNow = contextDateTimeNow;
        ThreadScopeStack.Value.Push(this);
    }

    public static DateTimeProviderContext Current
    {
        get
        {
            if (ThreadScopeStack.Value.Count == 0)
                return null;
            else
                return ThreadScopeStack.Value.Peek() as DateTimeProviderContext;
        }
    }

    public void Dispose()
    {
        ThreadScopeStack.Value.Pop();
    }
}

Why did we use ThreadLocal<Stack> to keep our stack? Because if we run our tests in parallel, they will run in separate threads and since we use a static field to hold the stack, they would interfere with each other and would cause mistakes to be made.

Inspired by akazemis.

Languages

EnglishEnglish
FrenchFrançais

Follow me

Recent posts