Better time-dependent testing with TimeProvider in .NET 8

Previously in .NET you might have had a class like this that you would inject in your classes that needed a predictable time in your unit tests:
public interface IMyDateTime
{
public DateTime Now();
public DateTime UtcNow();
}
public class MyDateTime : IMyDateTime
{
public DateTime Now() => DateTime.Now;
public DateTime UtcNow() => DateTime.UtcNow;
}
Wiring it up in the dependency injection:
builder.Services.AddSingleton<IMyDateTime, MyDateTime>();
And then, for instance, inject it into a service:
public class MyService(IMyDateTime myDateTime)
{
public DateTime DoSomething()
{
var currentLocalTime = myDateTime.Now();
return currentLocalTime;
}
}
Then in the unit test of the service, you could replace the implementation of IMyDateTime
with a an instance with a hard-coded value:
[TestClass]
public class MyServiceTest
{
[TestMethod]
public void DoSomething_DefaultCase_ReturnsLocalTime()
{
// Arrange
var myDateTime = new FakeDateTime(new DateTime(2025, 07, 10));
var service = new MyService(myDateTime);
// Act
var result = service.DoSomething();
// Assert
Assert.AreEqual(new DateTime(2025, 07, 10), result);
}
}
public class FakeDateTime(DateTime currentLocalTime) : IMyDateTime
{
public DateTime Now() => currentLocalTime;
public DateTime UtcNow() => currentLocalTime.ToUniversalTime();
}
However, in .NET 8, there’s now a class called TimeProvider. And for testing you can use FakeTimeProvider. Note though that FakeTimeProvider
is in the NuGet Package Microsoft.Extensions.TimeProvider.Testing
.
Using TimeProvider, you don’t have to create any custom interface and class anymore. Instead, you can change your service to the following:
public class MyService(TimeProvider timeProvider)
{
public DateTime DoSomething()
{
var currentLocalTime = timeProvider.GetLocalNow().LocalDateTime;
return currentLocalTime;
}
}
Register TimeProvider in the dependency injection:
builder.Services.AddSingleton(TimeProvider.System);
And do the unit test:
[TestClass]
public MyServiceTest
{
[TestMethod]
public void DoSomething_DefaultCase_ReturnsLocalTime()
{
// Arrange
var timeProvider = new FakeDateTime(new DateTime(2025, 07, 10));
var service = new MyService(timeProvider);
// Act
var result = service.DoSomething();
// Assert
Assert.Equals(result, new DateTime(2025, 07, 10));
}
}
Looks really clean!