Testing
TimeProvider
TimeProvider is an abstract base class introduced in .NET 8 that provides a standard way to access time-related information.
Key Features:
- Standardization: TimeProvider provides a unified approach to accessing time-related information, reducing the need for various custom implementations scattered across the codebase.
- Testability: By using a TimeProvider, time-dependent code can be easily tested. Developers can inject mock or custom implementations of TimeProvider to simulate different times and durations in unit tests.
- Flexibility: TimeProvider can be extended to create custom time providers that suit specific needs, such as simulating time in different time zones or providing consistent time sources in distributed systems.
- Separation of Concerns: It separates the concerns of obtaining the current time from business logic, making the code cleaner and more maintainable.
-
Create a new test project
dotnet new xunit -o TimeProviderTestProject
cd TimeProviderTestProject -
Add the necessary packages for testing
dotnet add package xunit
dotnet add package xunit.runner.visualstudio
dotnet add package Microsoft.Extensions.TimeProvider.Testing
dotnet add package Microsoft.Extensions.Diagnostics.Testing
dotnet add package AwesomeAssertions -
Domain - Create a Task object
public class Task
{
public required string Name { get; set; }
public DateTimeOffset DueDate { get; set; }
public bool IsOverdue()
{
var isOverdue = DueDate < TimeProvider.GetUtcNow();
return isOverdue;
}
public required TimeProvider TimeProvider { get; init; }
} -
Next, create a test using FakeTimeProvider
public class TimeProviderTests
{
[Fact]
public void TimeProviderTest_TaskIsOverdue_ReturnsFalse_WhenDueDateIsInFuture_Then_ReturnsTrue_WhenDueDateIsInPast()
{
var timeProvider = new FakeTimeProvider();
var task = new Task
{
Name = "Future Task",
DueDate = timeProvider.GetUtcNow().AddDays(1),
TimeProvider = timeProvider,
};
task.IsOverdue().Should().BeFalse();
timeProvider.Advance(TimeSpan.FromDays(5));
task.IsOverdue().Should().BeTrue();
}
}noteFakeTimeProvider allows us to shift the tests perspective of time, making it easy to test time-dependent logic.
FluentAssertions AwesomeAssertions is a library that provides a more readable and expressive way to write assertions in tests. It enhances the readability of test code by allowing developers to write assertions in a natural language style.
- Run the tests - see they pass
FakeLogger
Microsoft.Extensions.Logging.Testing provides a way to create fake loggers for testing purposes. This allows developers to verify that logging occurs as expected without needing to set up a real logging infrastructure.
-
Add Logger to the task and update the method to log a warning if the task is overdue
public class Task
{
public required string Name { get; set; }
public DateTimeOffset DueDate { get; set; }
public bool IsOverdue()
{
var isOverdue = DueDate < TimeProvider.GetUtcNow();
if (isOverdue)
{
Logger?.LogWarning("Task is overdue.");
}
return isOverdue;
}
public required TimeProvider TimeProvider { get; init; }
public ILogger<Task>? Logger { get; init; }
} -
Add a test to verify the logging functionality
[Fact]
public void TimeProviderTest_TaskIsOverdue_LogsWarning()
{
var timeProvider = new FakeTimeProvider();
var logger = new FakeLogger<Task>();
var task = new Task
{
Name = "Overdue Task",
DueDate = timeProvider.GetUtcNow().AddDays(1),
TimeProvider = timeProvider,
Logger = logger
};
task.IsOverdue();
logger.Collector.Count.Should().Be(0);
timeProvider.Advance(TimeSpan.FromDays(5));
task.IsOverdue();
logger.Collector.Count.Should().Be(1);
logger.LatestRecord.Message.Should().Be("Task is overdue.");
} -
Show the tests pass
Using, ILogger and FakeLogger allows us to test the logging functionality without needing to set up a real logging infrastructure - our code is testable out of the box.