How to mock DateTime in Unit Tests (C#)
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.