Introduction
In .NET 8, Microsoft introduced TimeProvider
, a new feature that simplifies testing time-based logic. Previously, mocking time often required third-party libraries or custom wrappers around DateTime
. This article provides a step-by-step guide to using TimeProvider
for more effective unit testing.
What is TimeProvider?
TimeProvider
is an abstract class that allows developers to override how time is provided in their applications. This enables better control over time for testing purposes. The default implementation, TimeProvider.System
, uses the system clock, but you can easily create custom implementations.
Example Scenario: Time-Based Greetings
We'll implement a service that provides greetings based on the current time, e.g., "Morning," "Afternoon," "Evening," and "Night."
Step 1: Create the Time-Based Service
Create a service class that categorizes greetings according to the time of day using TimeProvider
:
// UsingTime/TimeOfDayService.cs
namespace UsingTime
{
public class TimeOfDayService
{
private readonly TimeProvider _timeProvider;
public TimeOfDayService(TimeProvider timeProvider)
{
_timeProvider = timeProvider;
}
public string GetTimeOfDay()
{
var currentTime = _timeProvider.GetLocalNow();
return currentTime.Hour switch
{
<= 6 => "Night",
> 6 and <= 12 => "Morning",
> 12 and <= 18 => "Afternoon",
> 18 and <= 24 => "Evening",
_ => "Invalid hour"
};
}
}
}
Step 2: Create Custom Time Providers
Implement custom TimeProvider
classes to simulate different times of day for testing purposes:
// UsingTimeTest/NightTimeProvider.cs
namespace UsingTimeTest
{
public class NightTimeProvider : TimeProvider
{
public override DateTimeOffset GetUtcNow()
{
return new DateTimeOffset(2023, 12, 1, 1, 0, 0, TimeSpan.Zero); // 1 AM
}
}
}
Repeat this for other times:
// UsingTimeTest/MorningTimeProvider.cs
namespace UsingTimeTest
{
public class MorningTimeProvider : TimeProvider
{
public override DateTimeOffset GetUtcNow()
{
return new DateTimeOffset(2023, 12, 1, 8, 0, 0, TimeSpan.Zero); // 8 AM
}
}
}
// UsingTimeTest/AfternoonTimeProvider.cs
namespace UsingTimeTest
{
public class AfternoonTimeProvider : TimeProvider
{
public override DateTimeOffset GetUtcNow()
{
return new DateTimeOffset(2023, 12, 1, 16, 0, 0, TimeSpan.Zero); // 4 PM
}
}
}
// UsingTimeTest/EveningTimeProvider.cs
namespace UsingTimeTest
{
public class EveningTimeProvider : TimeProvider
{
public override DateTimeOffset GetUtcNow()
{
return new DateTimeOffset(2023, 12, 1, 20, 0, 0, TimeSpan.Zero); // 8 PM
}
}
}
Step 3: Write Unit Tests
Write unit tests to verify the TimeOfDayService
using these custom time providers.
// UsingTimeTest/TimeOfDayServiceTests.cs
using Xunit;
using UsingTime;
namespace UsingTimeTest
{
public class TimeOfDayServiceTests
{
private TimeOfDayService _timeOfDayService;
private TimeProvider _timeProvider;
[Fact]
public void TimeOfDay_ShouldReturnMorning_WhenItsMorning()
{
_timeProvider = new MorningTimeProvider();
_timeOfDayService = new TimeOfDayService(_timeProvider);
var result = _timeOfDayService.GetTimeOfDay();
Assert.Equal("Morning", result);
}
[Fact]
public void TimeOfDay_ShouldReturnAfternoon_WhenItsAfternoon()
{
_timeProvider = new AfternoonTimeProvider();
_timeOfDayService = new TimeOfDayService(_timeProvider);
var result = _timeOfDayService.GetTimeOfDay();
Assert.Equal("Afternoon", result);
}
[Fact]
public void TimeOfDay_ShouldReturnEvening_WhenItsEvening()
{
_timeProvider = new EveningTimeProvider();
_timeOfDayService = new TimeOfDayService(_timeProvider);
var result = _timeOfDayService.GetTimeOfDay();
Assert.Equal("Evening", result);
}
[Fact]
public void TimeOfDay_ShouldReturnNight_WhenItsNight()
{
_timeProvider = new NightTimeProvider();
_timeOfDayService = new TimeOfDayService(_timeProvider);
var result = _timeOfDayService.GetTimeOfDay();
Assert.Equal("Night", result);
}
}
}
Conclusion
With TimeProvider
in .NET 8, mocking time-based behavior is now easier and more standardized. Instead of relying on third-party tools or custom abstractions, developers can use the built-in API to manipulate time in unit tests. This results in more reliable and maintainable tests, simplifying the development process.
Top comments (1)
code sample
github.com/mohamedtayel1980/DotNet...
Unit test
github.com/mohamedtayel1980/DotNet...