avatarStephen Walsh

Summary

The undefined website presents a solution for mocking DateTime in .NET applications to facilitate better testing, introduced as the SimpleDateTimeProvider.

Abstract

The undefined website discusses the challenges of dealing with DateTime.Now and DateTime.UtcNow in .NET code, particularly when writing tests that require consistent time values. The author, Stephen Walsh, shares his approach to address this issue by creating a DateTimeProvider interface with two implementations: SystemDateTimeProvider for real system time and MockDateTimeProvider for controlled time values in tests. This solution is encapsulated in the SimpleDateTimeProvider NuGet package, which allows developers to inject the appropriate provider and write testable code that can mock the current date and time. The article also acknowledges the inspirations behind this approach and provides guidance on how to use the package with examples, including integration with dependency injection and writing tests using XUnit and Shouldly.

Opinions

  • The author, Stephen Walsh, expresses that dealing with real-time values in .NET can be an annoying problem when testing code, emphasizing the need for consistent time values.
  • Walsh credits the inspiration for his solution to Andrew Harcourt, Andrew Wickens, and Jared Wilton, recognizing their contributions to his approach.
  • The author believes that the SimpleDateTimeProvider package simplifies the process of mocking date and time in

Bending DateTime in .NET to Test Your Code Better

Mocking DateTime in .NET is painful, here’s a way to make it hurt less.

Okay I’ll dial down the hyperbole, over my years (read I’m feeling old) as an engineer I have run into a problem where my code has contained a DateTime.Now or DateTime.UtcNow. When I write a test, I can't validate the actual time because milliseconds have passed between the code running and validating.

It's not a huge problem but annoying as I like to validate everything to make sure I'm not accidentally manipulating those values somewhere else.

There is an easy solution to this, and before I detail the solution that I used, I need to call out the inspirations for this. Thanks to Andrew Harcourt, Andrew Wickens and Jared Wilton for giving me that extra push.

The TL;DR; is that you can consume my SimpleDateTimeProvider NuGet package to help you solve this. The implementation of the code for this lies below.

The Solution

I have created a DateTimeProvider consisting of an interface, and two implementations of the interface. One implementation returns the System values and the other returns Mocked values that are preset by the user.

The Interface

public interface IDateTimeProvider
{
  DateTime Now { get; }
  DateTime Today { get; }
  DateTime UtcNow { get; }
}

The System Implementation

public class SystemDateTimeProvider : IDateTimeProvider
{
  public DateTime Now => DateTime.Now;
  public DateTime Today => DateTime.Today;
  public DateTime UtcNow => DateTime.UtcNow;
}

The Mock Implementation

public class MockDateTimeProvider : IDateTimeProvider
{
  public DateTime Now
  {
    get => this.now.ThrowIfNotSet(DateTimeType.Now);
    set => this.now = value;
  }

  public DateTime Today
  {
    get => this.today.ThrowIfNotSet(DateTimeType.Today);
    set => this.today = value;
  }

  public DateTime UtcNow
  {
    get => this.utcNow.ThrowIfNotSet(DateTimeType.UtcNow);
    set => this.utcNow = value;
  }
}

Bending Date and Time

This is a simple solution and it’s easy to get under way using the providers, simply inject the system provider under the IDateTimeProvider interface in your functional code. If you are using another dependency injection library, you'll know the syntax, just follow the same formula.

_ = builder.Services.AddSingleton<IDateTimeProvider, SystemDateTimeProvider>();

Next step is to create your class and use that registered SystemDateTimeProvider that we just created via the IDateTimeProvider interface. Then use the provider to set the DateTime values in your class.

public class Service
{
  private readonly IDateTimeProvider dateTimeProvider;
  
  public Service(IDateTimeProvider dateTimeProvider)
  {
    this.dateTimeProvider = dateTimeProvider;
  }

  public string DateTimeNow()
  {
    return $"DateTime.Now is {this.dateTimeProvider.Now}";
  }
}

The whole purpose of this was to allow for testable code. So now that you have your class above, you can inject the MockDateTimeProvider in its place to control the DateTime values in your tests. The following example shows how to write a test in XUnit, using Shouldly for assertion.

[Fact]
public void Today_ShouldReturn_MockedToday()
{
  // Arrange
  var provider = new MockDateTimeProvider();
  var service = new Service(provider);
  var today = DateTime.Today;

  provider.Today = today;

  // Act
  var result = service.DateTimeToday();

  // Assert
  _ = result.ShouldBeOfType<string>();
  result.ShouldBe($"DateTime.Today is {today}");
}

Where Can I Find This?

All of this open sourced. You can find my work on GitHub at the SimpleDateTimeProvider respository and the NuGet package at SimpleDateTimeProvider.

Connect or Support?

If you like this, or want to check out my other work, please connect with me on LinkedIn or GitHub. I’d love it if you’d consider supporting me too, all I’ll ever ask for is $1 on GitHub Sponsors or Buy Me a Coffee.

Software Engineering
Software Development
Technology
Programming
Coding
Recommended from ReadMedium