$$ \newcommand\Tr{\mathrm{Tr}} \newcommand{\braket}[2]{\langle #1 \mid #2 \rangle} \newcommand\I{\mathbb{I}} \newcommand{\avg}[1]{\left< #1 \right>} \newcommand{\RD}{D} \newcommand{\ri}{\mathrm{i}} \DeclareMathOperator{\sign}{sign} \DeclareMathOperator{\Sign}{Sign} \newcommand{\ii}{\mathrm i} \newcommand{\vv}{\mathrm v} \newcommand{\ff}{\mathrm f} \newcommand{\mm}{\mathrm m} \newcommand{\ee}{\mathrm e} \newcommand{\xx}{\mathrm x} \newcommand{\RR}{\mathrm R} \newcommand{\dd}{\mathrm d} \newcommand{\FF}{\mathrm F} \newcommand{\BB}{\mathrm B} \newcommand{\vph}{v_{\mathrm{ph}}} $$

How to Mock Static Method or Property in C#

Problem

In C#, it is difficult when we want to test a method that contains DateTime.Now because we cannot control its behaviour.
Considering below code, the value of DateTime.Now will depend on the time the code is executed.
So, when we test on today 10:15:00 AM, the expectation will be 10:16:00 AM. But, when this is executed on CI server later, the expectation won’t be 10:16:00 AM, which will fail our tests.

internal class Program
{
    static void Main(string[] args)
    {
        TimeProvider timeProvider = new TimeProvider();
        DateTime dateTimeNow = timeProvider.GetNextOneMinuteFromNow();
        Console.WriteLine(dateTimeNow);
    }
}

public class TimeProvider
{
    public DateTime GetNextOneMinuteFromNow()
    {
        return DateTime.Now.AddMinutes(1);
    }
}

Solution

There are 2 solutions we can use:

  1. Create a wrapper class and apply dependency injection
  2. Use Shims as part of Microsoft Fakes Framework

Create a wrapper class and apply dependency injection

Considering below code, we do these modifications:

  1. Add interface IDateTimeNowProvider that abstracts concrete class that will be injected to TimeProvider class
  2. Add class DateTimeNowProvider as concrete implementation of IDateTimeNowProvider interface
  3. Inject DateTimeNowProvider to TimeProvider class

internal class Program
{
    static void Main(string[] args)
    {
        TimeProvider timeProvider = new TimeProvider(new DateTimeNowProvider());
        DateTime dateTimeNow = timeProvider.GetNextOneMinuteFromNow();
        Console.WriteLine(dateTimeNow);
    }
}

public interface IDateTimeNowProvider
{
    DateTime DateTimeNow { get; }
}

public class DateTimeNowProvider : IDateTimeNowProvider
{
    public DateTime DateTimeNow => DateTime.Now;
}

public class TimeProvider
{
    public IDateTimeNowProvider DateTimeNowProvider { get; }

    public TimeProvider(IDateTimeNowProvider dateTimeNowProvider)
    {
        DateTimeNowProvider = dateTimeNowProvider;
    }

    public DateTime GetNextOneMinuteFromNow()
    {
        return DateTimeNowProvider.DateTimeNow.AddMinutes(1);
    }
}

We also add unit test showing how to test class that wraps DateTime.Now. We use Moq as mocking framework.

[TestMethod()]
public void Get_Next_Minute_From_Current_Time()
{
    // Arrange
    var dateTimeNowProvider = new Mock<IDateTimeNowProvider>();
    dateTimeNowProvider.Setup(x => x.DateTimeNow).Returns(new DateTime(2022, 6, 12, 11, 15, 0));
    TimeProvider timeProvider = new TimeProvider(dateTimeNowProvider.Object);

    // Act
    DateTime actual = timeProvider.GetNextOneMinuteFromNow();

    // Assert
    DateTime exptected = new DateTime(2022, 6, 12, 11, 16, 0);
    Assert.AreEqual(exptected, actual);
}

Use Shims as part of Microsoft Fakes Framework

Microsoft Fakes has caveat that it's only available in Visual Studio Enterprise.
Other than that, you cannot use Shims, e.g., Visual Studio Professional, Community, JetBrains Rider, etc.

We need to do these in order to use Shims:

  1. Add fakes assembly of System.Runtime where DateTime class/struct resides.
    This applies for .NET 6.0. For previous version, typically DateTime resides on mscorlib. datetime-system-runtime-assembly

  2. Modify System.Runtime.fakes file that is generated after we add fakes assembly above

    <Fakes xmlns="http://schemas.microsoft.com/fakes/2011/">
      <Assembly Name="System.Runtime" Version="6.0.0.0"/>
      <StubGeneration>
        <Clear/>
      </StubGeneration>
      <ShimGeneration>
        <Clear/>
        <Add FullName="System.DateTime"/>
      </ShimGeneration>
    </Fakes>
    

  3. Add fakes unit test using Shims

    [TestMethod()]
    public void Get_Next_Minute_From_Current_Time()
    {
        using (ShimsContext.Create())
        {
            // Arrange
            System.Fakes.ShimDateTime.NowGet =
                            () =>
                            { return new DateTime(2022, 6, 12, 11, 15, 0); };
    
            // Act
            TimeProvider timeProvider = new TimeProvider();
            DateTime actual = timeProvider.GetNextOneMinuteFromNow();
    
            // Assert
            DateTime expected = new DateTime(2022, 6, 12, 11, 16, 0);
            Assert.AreEqual(expected, actual);
        }
    }
    
    We must prefix the class that we shim with Shim, e.g., ShimDateTime. For static getter property, we append the name of the property Now with Get to be NowGet.
    The property or method that we shim must also be inside ShimsContext and using statement.
    So that when it goes out of the scope, the Shim behaviour is removed. Otherwise, DateTime.Now value is always the same in every test we create.

Source Code

The source can be found in the github.