Test with Moq and AutoFixture

In the previous articles of this series, we explored the fundamental concepts of unit testing using handcrafted Test Doubles. We implemented different types of Test Doubles, such as Dummy, Stub, Spy, and Mock. Now that we’ve covered the basics, understood their importance, and learned how and when to use them, it’s time to elevate our testing strategy. By leveraging libraries and frameworks widely accepted in the community, such as Moq and AutoFixture, we can simplify the creation of test doubles, enhance maintainability, and reduce boilerplate code. Let’s dive into how these tools work and how their combination can streamline your unit testing process. What's Moq? Moq (pronounced "Mock-you" or simply "Mock") is a widely used library for dynamically creating strongly typed Test Doubles. With Moq, you can create Dummies, Stubs, and Mocks "on the fly." Creating a Dummy with Moq When you instantiate a Mock object without setting up any behavior for its properties or methods, Moq treats it as a Dummy, returning default values for the invoked methods: var weatherService = new Mock(); Evolving a Dummy into a Stub or Mock You can configure the behavior of the Mock object to simulate real-world scenarios. For instance: List expectedForecast = [new WeatherForecast()]; _weatherService.Setup(x => x.GetByCity(_cityWithData)) .Returns(expectedForecast) .Verifiable(Times.Once); This code configures the GetByCity method to return the expectedForecast list when called with _cityWithData, and ensures the call is verifiable (expected to be invoked exactly once). Verifying Behavior At the end of your tests, you can verify if the configured expectations were met: _weatherService.Verify(); This will throw an exception if the expectations are not satisfied. Using the Mock Object You might wonder: "We are working with a Mock, but the WeatherForecastController expects an instance of IWeatherService!" You are right! You can't pass the Mock to the controller constructor, instead you pass the actual IWeatherService object created from the Mock: _weatherService.Object Here’s the complete test class demonstrating basic usage of Moq: public class TestWithMoq { private readonly WeatherForecastController _sut; private readonly Mock _weatherService; private readonly string _cityWithData = "Rome"; public TestWithMoq() { _weatherService = new Mock(); _sut = new WeatherForecastController(_weatherService.Object, NullLogger.Instance); } [Fact] public void Get_ReturnOk_When_Data_Exists() { List expectedForecast = [new WeatherForecast()]; _weatherService.Setup(x => x.GetByCity(_cityWithData)) .Returns(expectedForecast) .Verifiable(Times.Once); IActionResult actual = _sut.Get(_cityWithData); var okResult = Assert.IsType(actual); var forecasts = Assert.IsAssignableFrom(okResult.Value); Assert.NotEmpty(forecasts); Assert.Same(expectedForecast, forecasts); _weatherService.Verify(); } [Fact] public void Get_ReturnNoContent_When_Data_NotExists() { IActionResult actual = _sut.Get("Paris"); Assert.IsType(actual); } } In the Get_ReturnNoContent_When_Data_NotExists we are using the Mock object as a Dummy and the GetByCity is returning null. Moving the setup of the Mock from the first test to the constructor of the test class will not change this behavior because the mock will respond null for all input except for "Rome"! This example highlights Moq’s capabilities to create maintainable and readable tests. For further exploration of Moq’s features, check out the Quickstart on GitHub. Moq + AutoFixture = ❤️ Introducing AutoFixture One of the biggest challenges in unit testing is maintaining the tests as your code evolves. Refactoring constructors or adding new dependencies often breaks test compilation, even if the business logic hasn’t changed. This is where AutoFixture comes in. AutoFixture is a powerful library that acts as a dynamic object factory, generating random yet valid data and objects for your tests. Here are some basic examples: Fixture fixture = new Fixture(); IEnumerable expectedForecasts = fixture.CreateMany(); string city = fixture.Create(); int random = fixture.Create(); AutoFixture simplifies object creation, letting you focus on the test logic rather than the setup. Combining Moq and AutoFixture The magic happens when you mix Moq and AutoFixture. With the AutoFixture.AutoMoq NuGet package, you can integrate Moq into AutoFixture seamlessly. This allows AutoFixture to create Mock objects and even instantiate your system under test (SUT). Here’s the refactored test class using AutoFixture and Moq: public class TestWithAutoFixtureMoq { private readonly IFixture _fixture; public TestWithAutoFixtureM

Jan 18, 2025 - 19:53
Test with Moq and AutoFixture

In the previous articles of this series, we explored the fundamental concepts of unit testing using handcrafted Test Doubles. We implemented different types of Test Doubles, such as Dummy, Stub, Spy, and Mock.
Now that we’ve covered the basics, understood their importance, and learned how and when to use them, it’s time to elevate our testing strategy.
By leveraging libraries and frameworks widely accepted in the community, such as Moq and AutoFixture, we can simplify the creation of test doubles, enhance maintainability, and reduce boilerplate code. Let’s dive into how these tools work and how their combination can streamline your unit testing process.

What's Moq?

Moq (pronounced "Mock-you" or simply "Mock") is a widely used library for dynamically creating strongly typed Test Doubles. With Moq, you can create Dummies, Stubs, and Mocks "on the fly."

Creating a Dummy with Moq

When you instantiate a Mock object without setting up any behavior for its properties or methods, Moq treats it as a Dummy, returning default values for the invoked methods:

var weatherService = new Mock<IWeatherService>();

Evolving a Dummy into a Stub or Mock

You can configure the behavior of the Mock object to simulate real-world scenarios. For instance:

List<WeatherForecast> expectedForecast = [new WeatherForecast()];
_weatherService.Setup(x => x.GetByCity(_cityWithData))
    .Returns(expectedForecast)
    .Verifiable(Times.Once);

This code configures the GetByCity method to return the expectedForecast list when called with _cityWithData, and ensures the call is verifiable (expected to be invoked exactly once).

Verifying Behavior

At the end of your tests, you can verify if the configured expectations were met:

_weatherService.Verify();

This will throw an exception if the expectations are not satisfied.

Using the Mock Object

You might wonder: "We are working with a Mock, but the WeatherForecastController expects an instance of IWeatherService!"

You are right! You can't pass the Mock to the controller constructor, instead you pass the actual IWeatherService object created from the Mock:

_weatherService.Object

Here’s the complete test class demonstrating basic usage of Moq:

public class TestWithMoq
{
    private readonly WeatherForecastController _sut;
    private readonly Mock<IWeatherService> _weatherService;
    private readonly string _cityWithData = "Rome";

    public TestWithMoq()
    {
        _weatherService = new Mock<IWeatherService>();
        _sut = new WeatherForecastController(_weatherService.Object, NullLogger<WeatherForecastController>.Instance);
    }

    [Fact]
    public void Get_ReturnOk_When_Data_Exists()
    {
        List<WeatherForecast> expectedForecast = [new WeatherForecast()];
        _weatherService.Setup(x => x.GetByCity(_cityWithData))
            .Returns(expectedForecast)
            .Verifiable(Times.Once);

        IActionResult actual = _sut.Get(_cityWithData);

        var okResult = Assert.IsType<OkObjectResult>(actual);
        var forecasts = Assert.IsAssignableFrom<IEnumerable<WeatherForecast>>(okResult.Value);
        Assert.NotEmpty(forecasts);
        Assert.Same(expectedForecast, forecasts);
        _weatherService.Verify();
    }

    [Fact]
    public void Get_ReturnNoContent_When_Data_NotExists()
    {
        IActionResult actual = _sut.Get("Paris");

        Assert.IsType<NoContentResult>(actual);
    }
}

In the Get_ReturnNoContent_When_Data_NotExists we are using the Mock object as a Dummy and the GetByCity is returning null.
Moving the setup of the Mock from the first test to the constructor of the test class will not change this behavior because the mock will respond null for all input except for "Rome"!

This example highlights Moq’s capabilities to create maintainable and readable tests. For further exploration of Moq’s features, check out the Quickstart on GitHub.

Moq + AutoFixture = ❤️

Introducing AutoFixture

One of the biggest challenges in unit testing is maintaining the tests as your code evolves. Refactoring constructors or adding new dependencies often breaks test compilation, even if the business logic hasn’t changed. This is where AutoFixture comes in.

AutoFixture is a powerful library that acts as a dynamic object factory, generating random yet valid data and objects for your tests. Here are some basic examples:

Fixture fixture = new Fixture();
IEnumerable<WeatherForecast> expectedForecasts = fixture.CreateMany<WeatherForecast>();
string city = fixture.Create<string>();
int random = fixture.Create<int>();

AutoFixture simplifies object creation, letting you focus on the test logic rather than the setup.

Combining Moq and AutoFixture

The magic happens when you mix Moq and AutoFixture.
With the AutoFixture.AutoMoq NuGet package, you can integrate Moq into AutoFixture seamlessly. This allows AutoFixture to create Mock objects and even instantiate your system under test (SUT).

Here’s the refactored test class using AutoFixture and Moq:

public class TestWithAutoFixtureMoq
{
    private readonly IFixture _fixture;

    public TestWithAutoFixtureMoq()
    {
        _fixture = new Fixture().Customize(new CompositeCustomization(
                new AutoMoqCustomization(),
                new ConstructorCustomization(typeof(WeatherForecastController), new GreedyConstructorQuery())
            ));
        _fixture.Customize<DateOnly>(c => c.FromFactory((DateTime dt) => DateOnly.FromDateTime(dt)));
    }

    [Fact]
    public void Get_ReturnOk_When_Data_Exists()
    {
        var expectedForecasts = _fixture.CreateMany<WeatherForecast>().ToList();
        var city = _fixture.Create<string>();

        var weatherService = _fixture.Freeze<Mock<IWeatherService>>();
        weatherService.Setup(x => x.GetByCity(city))
            .Returns(expectedForecasts)
            .Verifiable(Times.Once);

        var sut = _fixture.Create<WeatherForecastController>();

        IActionResult actual = sut.Get(city);

        var okResult = Assert.IsType<OkObjectResult>(actual);
        var forecasts = Assert.IsAssignableFrom<IEnumerable<WeatherForecast>>(okResult.Value);
        Assert.NotEmpty(forecasts);
        Assert.Same(expectedForecasts, forecasts);
        weatherService.Verify();
    }
}

As you can see, we let AutoFixture create all the objects we need, WeatherForecastController included; this way we can make all the changes we want to the WeatherForecastController constructor without the need to change the test code!
This approach ensures that your tests remain resilient to changes in constructors or dependencies.

The only thing we need to do is customize the Fixture object we are gonna use as our "magic object factory":

_fixture = new Fixture().Customize(new CompositeCustomization(
        new AutoMoqCustomization(),
        new ConstructorCustomization(typeof(WeatherForecastController), new GreedyConstructorQuery())
    ));
_fixture.Customize<DateOnly>(c => c.FromFactory((DateTime dt) => DateOnly.FromDateTime(dt)));

With these lines of code we are customizing the fixture with:

  • AutoMoqCustomization so that AutoFixture knows how to create Moq objects
  • ConstructorCustomization to customize how the WeatherForecastController will be created. By specifying a "greedy" policy, we ensure that AutoFixture selects the constructor with the most parameters, instead of the default behavior, which picks the one with the fewest. In an ASP.NET Core test context, this is crucial to avoid runtime exceptions, as controllers typically rely on constructors that accept multiple dependencies. Using the "greedy" policy ensures that all required dependencies are properly initialized.
  • At the end we are customizing how we want a DateOnly value to be created, that is from a random DateTime object.

Now you can follow the code of the test and the only new think that you will find is this line:

var weatherService = _fixture.Freeze<Mock<IWeatherService>>();

The AutoFixture Freeze method will create and freezes the type to always return the same instance whenever an instance of the type is requested either directly, or indirectly as a nested value of other types.

This way, when the _fixture create the WeatherForecastController will use the IWeatherService object coming from the Mock we have already set up.

Advanced Scenarios with AutoFixture

We can further refactor our test, reducing the code, with the power of Theory and AutoData.
The Theory Attribute let you inject data, from different data source, to your test method thurgh method parameter. An example with InlineData could be this:

[Theory]
[InlineData(1, 2, 3)]
[InlineData(4, 5, 9)]
public void Sum(int a, int b, int expectedResult)
{
    int result = a + b;
    Assert.Equal(expectedResult, result);
}

The AutoData Attribute comes from AutoFixture and provide auto generate data from a Fixture. We can create a custom AutoData Attribute to combine the fixture with Moq to enable even more concise and data-driven tests:

public class AutoMoqDataAttribute : AutoDataAttribute
{
    public AutoMoqDataAttribute()
      : base(CreateFixture)
    {
    }

    private static IFixture CreateFixture()
    {
        var fixture = new Fixture().Customize(new AutoMoqCustomization());

        fixture.Customize<DateOnly>(c => c.FromFactory((DateTime dt) => DateOnly.FromDateTime(dt)));

        return fixture;
    }
}

Now we can use AutoMoqData as an attribute and refactor our test this way:

[Theory]
[AutoMoqData]
public void Get_ReturnOk_With_AutoMoqData(
    [Frozen] Mock<IWeatherService> weatherService, 
    [Greedy] WeatherForecastController sut, 
    string city,
    List<WeatherForecast> expectedForecasts)
{
    weatherService.Setup(x => x.GetByCity(city))
        .Returns(expectedForecasts)
        .Verifiable(Times.Once);

    IActionResult actual = sut.Get(city);

    var okResult = Assert.IsType<OkObjectResult>(actual);
    var forecasts = Assert.IsAssignableFrom<IEnumerable<WeatherForecast>>(okResult.Value);
    Assert.Same(expectedForecasts, forecasts);
    weatherService.Verify();
}

You can read more about AutoFixture and all the other integration libraries to the offical documentation page.

Conclusion

Throughout this series, we’ve explored foundational and advanced unit testing concepts, starting with handcrafted Test Doubles and progressing to Moq and AutoFixture. These tools empower developers to:

  • Write tests that are easier to read and maintain.
  • Reduce boilerplate and focus on test logic.
  • Handle changes in dependencies with minimal friction.

Mastering unit testing is an ongoing journey. With tools like Moq and AutoFixture, you can build a robust, maintainable test suite that supports your applications’ growth.
Start applying these concepts today and elevate your testing strategy!