Introduction

Writing unit tests around dates and times is hard. I have written about it before around Mocking The Clock and to use an interface around DateTime and DateTimeOffset etc. A similar technique has been used by many in the past and in earlier versions of the frameworks Microsoft wrote they had similar concepts. However with .NET 8 they brought out the concept of TimeProvider for a consistent usage.

I’m not going to go into detail about what the TimeProvider is or how to use it in this post as there are many other blog posts out there. What I am going to explain is an oddity I discovered with using TimeProvider and AutoFixture’s AutoMoq functionality which I wasn’t expecting.

What these libraries allow you to do is automatically setup a mock for an interface or abstract class and then use it, through the mechanism of “freezing”, when asking AutoFixture of an instance of your target type. This allows referencing the same instance and setting up expectations as you would normally do with a mock.

The Problem

As TimeProvider is an abstract class it, like interfaces, are able to be mocked. As I am a big fan of auto generating data and mocks etc. through the attribute decorations and extensions which xUnit and AutoFixture give you I thought can it be done with TimeProvider. Conceptually this should be doable as it’s an abstract class but for some reason I couldn’t get it to work.

Let me explain through an example of the setup.

In this example I will be using the following as sample constructs. As you can see a very simple Wrapper<T> class which is purely there to wrap an abstract class and return the instance of the type it is constructed with.

public abstract class WasHere
{
    protected WasHere() { }
}

public class Wrapper<T> where T : class
{
    public Wrapper(T myAbstractType)
    {
        Type = myAbstractType;
    }

    public T Type { get; }
}

A test which demonstrates the scenario I was fighting against.

[Fact]
public void TestWithFixture()
{
    var fixture = new Fixture();
    fixture.Customize(new AutoMoqCustomization());

    var at = fixture.Freeze<Mock<WasHere>>();

    var sut = fixture.Create<Wrapper<WasHere>>();

    // success
    sut.Type.Should().Be(at.Object);
}

In this test we are creating a new instance of the Fixture class which is the main entry point into AutoFixture. We are then registering the AutoMoqCustomization customization which allows for working with Mocks of targeted types.

After the setup we are requesting an instance the Mock<WasHere>. However instead of using the Create method we are using the Freeze method. What this does is create an instance and “record” it for future use. If another call to Create requires this type in the current scope it will use that instance instead. This allows for the interaction with the same instance which is then used.

Once the mock has been setup we then request the wrapper which is our SUS, or system under test, to allow for the checking of the instance which was passed into the constructor.

This test passes as expected.

Now let’s try with TimeProvider.

[Fact]
public void TestWithFixtureTimeProvider()
{
    var fixture = new Fixture();
    fixture.Customize(new AutoMoqCustomization());

    var at = fixture.Freeze<Mock<TimeProvider>>();

    var sut = fixture.Create<Wrapper<TimeProvider>>();

    // fails
    sut.Type.Should().Be(at.Object);
}

As we can see the setup and construction of the test is exactly the same except for the generic argument for setting up the mock for TimeProvider and Wrapper<TimeProvider> when requesting the sut.

You maybe surprised to know this test fails.

Why does it fail?

You maybe looking at the failing test scenario and be thinking “but why?”. This was the same thought I came across as well. I had mainly used these concepts with interfaces and mocks before but was pretty sure abstract classes should work as well.

Was TimeProvider a special type of abstract class? Was there some oddities about how my project was setup? Had I done something wrong?

I even ended up cloning the source code of the AutoFixture repository and spent time debugging through the code to try and determine the issue.

The Solution?

After it getting late and admitting defeat I wrote up the unit tests above to prove and reproduce the problem and posted a defect on the GitHub issues page and went to bed - Issue link. Andrei Ivascu replied over night with the answer! Thanks Andrei!

I wasn’t doing anything wrong but it was more down to the way TimeProvider worked, how it knew about the default implementation (should it?) and how AutoFixture didn’t expect abstract classes to be written like that.

What is going on is AutoFixture knows how to create constructs through either constructors or through factory functions. What Autofixture is seeing is that the static property on TimeProvider, the System property, is returning a new instance of the SystemTimeProvider and this is being used instead of the expected mocking functionality for the abstract class.

The Workaround

In the same discussion Renato Golia replied with a workaround.

Thier suggestion was to add an additional registration of the mock so that the generating code could use it.

[Fact]
public void TestWithFixtureTimeProvider()
{
    var fixture = new Fixture();
    fixture.Customize(new AutoMoqCustomization());

    var at = fixture.Freeze<Mock<TimeProvider>>();
    fixture.Register(() => at.Object);

    var sut = fixture.Create<Wrapper<TimeProvider>>();

    // fails
    sut.Type.Should().Be(at.Object);
}

As you can see above the Freeze call we are now having to register the mock object manually. The Register method expects a Func<T> where the T is the type you are looking to specify so in this instance we have to set the at.Object property as the instance we want to register.

For me the signature of the Register method looks a little clunky in this scenario. However while I was splunking around the codebase I noticed an extension method which encapsulates this method called Inject (link) so it makes the calling code less verbose.

[Fact]
public void TestWithFixtureTimeProvider()
{
    var fixture = new Fixture();
    fixture.Customize(new AutoMoqCustomization());

    var at = fixture.Freeze<Mock<TimeProvider>>();
    fixture.Inject(at.Object);

    var sut = fixture.Create<Wrapper<TimeProvider>>();

    // fails
    sut.Type.Should().Be(at.Object);
}

Conclusion

The new TimeProvider construct is overall a good thing for .NET. We’ve been looking for a consistent way to test logic based on dates, times and timers for a long time so it’s good there is now something out of the box. However due to the design decision around having an known implementation on the abstract type (thoughts?) it has made this library not behave how you would expect.

In this scenario we have found a work around however this still shouldn’t be needed so I will be keeping an eye on what the proper solution will be. As for now this will do but I am now not sure how using it with the [Theory] based DataAttributes will work when all you have is the ability to customize how the Fixture instance will behave and not access to the actual instances.

What are you thoughts on this? How have you solved this?


It’s worth noting that I did try using the NSubstitute equivalent constructs and they did behave as expected out of the box. Please find the set below if anyone stumbles across this post and is using NSubstitute.

[Fact]
public void TestWithFixtureNSub()
{
    var fixture = new Fixture();
    fixture.Customize(new AutoNSubstituteCustomization());

    var at = fixture.Freeze<WasHere>();

    var sut = fixture.Create<Wrapper<WasHere>>();

    // success
    sut.Type.Should().Be(at);
}

[Fact]
public void TestWithFixtureTimeProviderNSub()
{
    var fixture = new Fixture();
    fixture.Customize(new AutoNSubstituteCustomization());

    var at = fixture.Freeze<TimeProvider>();

    var sut = fixture.Create<Wrapper<TimeProvider>>();

    // success
    sut.Type.Should().Be(at);
}