In this chapter on unit testing, I’m going to discuss Moq and how it can effectively be used to ensure you are unit testing correctly without having to concern yourself (too much) with dependencies further down the chain.

Sample Project

I’ve used the base ASP.NET Code Web API project to start things off and made a few modifications to it, all of which can be found in the repo below:

https://github.com/Wallyza/EasyTests/tree/v2.0

Refactoring the service

The first thing I want to do is refactor our WeatherForecastService.cs to extract the Summaries array to be returned by another service. Imagine a service that calls a public API to return these summaries.

First, we create the WeatherSummariesService class:

namespace EasyTests.Services;

public interface IWeatherSummariesService
{
    string[] GetWeatherSummaries();
}

public class WeatherSummariesService : IWeatherSummariesService
{
    private static readonly string[] Summaries = new[]
    {
        "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
    };

    public string[] GetWeatherSummaries()
    {
        return Summaries;
    }
}

Pretty straightforward, for all intents and purposes. Now we change our WeatherForecastService class to have this as a dependency:

namespace EasyTests.Services;

public interface IWeatherForecastService
{
    IEnumerable<WeatherForecast> GetWeatherForecast();
}

public class WeatherForecastService : IWeatherForecastService
{
    private readonly IWeatherSummariesService _weatherSummariesService;

    public WeatherForecastService(IWeatherSummariesService weatherSummariesService)
    {
        _weatherSummariesService = weatherSummariesService;
    }
    
    public IEnumerable<WeatherForecast> GetWeatherForecast()
    {
        var weatherSummaries = _weatherSummariesService.GetWeatherSummaries();
        
        return Enumerable.Range(1, 5).Select(index => new WeatherForecast
            {
                Date = DateTime.Now.AddDays(index),
                TemperatureC = Random.Shared.Next(-20, 55),
                Summary = weatherSummaries[Random.Shared.Next(weatherSummaries.Length)]
            })
            .ToArray();
    }
}

Yet again, we’re adding complication but we’re also separating concerns. The same can be done for TemperatureC but we’ll worry about that at a later stage.

Lastly, don’t forget to add the new WeatherSummariesService to your IOC container in Program.cs:

builder.Services.AddTransient<IWeatherSummariesService, WeatherSummariesService>();

The last thing we need to do is update our unit tests to reflect the new WeatherForecastService constructor:

    private readonly WeatherForecastService _testClass = new WeatherForecastService(new WeatherSummariesService());

For now, we’ll simply create a new instance of the WeatherSummariesService but as you can imagine, this is not ideal, but more on that in a bit.

Now if we run dotnet build we should have 0 issues and all our unit tests should pass.

IOC in Unit Tests

Of course, it’s messy when we simply inject a new instance of WeatherSummariesService into our constructor. Firstly we’re overriding the purpose of dependency injection (what would happen if we swapped out our implementation of IWeatherSummariesService but forgot to do the same in our unit tests?) and second we’re testing outside of our scope.

Our unit test GetWeatherForecast_None_Returns5 should be testing the logic in GetWeatherForecast, not in its dependencies.

Time to mock some dependencies

Time to look at one of my favourite unit-testing libraries, Moq.

Moq will mock an implementation of our interfaces without actually executing any real logic. What is the one thing we can (should) rely on with our dependencies? They’ll always adhere to the contract (our interfaces).

Implement Moq

First, we install the Moq Nuget package and include it in our usings:

using Moq;

Then we can mock out our WeatherSummariesService as follows:

    private readonly Mock<IWeatherSummariesService> _weatherSummariesServiceMock = new Mock<IWeatherSummariesService>();

To implement this mock in our test class it’s easier to move things into our constructor as follows:

    private readonly Mock<IWeatherSummariesService> _weatherSummariesServiceMock = new Mock<IWeatherSummariesService>();
    private readonly WeatherForecastService _testClass;

    public WeatherForecastServiceTests()
    {
        _testClass = new WeatherForecastService(_weatherSummariesServiceMock.Object);
    }

_weatherSummariesServiceMock.Object is returning the mocked implementation of the IWeatherSummariesService interface. So far, so good but if we run our unit tests they all fail!

Mock can mock an implementation but it’s not going to return mock data, which would obviously skew our tests and produce unreliable results.

We can add our own behaviour though. By calling _weatherSummariesServiceMock.Setup(...) we’re able to define the implementation behaviour and supply mocked responses.

Let’s do that and see how that affects our unit tests. We’ll update our controller to look like below:

    public WeatherForecastServiceTests()
    {
        _weatherSummariesServiceMock
            .Setup(x => x.GetWeatherSummaries())
            .Returns(new[] {"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"});
        _testClass = new WeatherForecastService(_weatherSummariesServiceMock.Object);
    }

All we’re doing is telling the implementation to return that particular array and now our unit tests are all passing.

Moq conclusion

Now no matter what our underlying implementation of WeatherSummariesService changes to, as long as it adheres to our contract, our unit tests will now pass. This way we can refrain from calling external APIs on every unit test pass or even doing simple CRUD on an actual database.

More advanced tests with Moq

Verifiable methods

Let’s say you want to ensure that within the logic of a test, a specific method is called a specific number of times. Thanks to Moq we can ensure that a method is verifiable. Let’s add a unit test specifically for this sort of scenario.

First, we want to make the implementation verifiable:

        _weatherSummariesServiceMock
            .Setup(x => x.GetWeatherSummaries())
            .Returns(new[] {"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"})
            .Verifiable();

Now, we can write our new unit test:

    [Fact]
    public void GetWeatherForecast_None_GetSummariesCalledOnce()
    {
        // arrange
        
        // act
        var response = _testClass.GetWeatherForecast();
        
        // assert
        _weatherSummariesServiceMock.Verify(x => x.GetWeatherSummaries(), Times.Once);
    }

This is great when we want to make sure a database isn’t written to unnecessarily or a user doesn’t get multiple push notifications for the same event.

Error testing

Sometimes we want to ensure that the error behaviour of a method is testable. Let’s change our GetWeatherForecast code to reflect the following:

    public IEnumerable<WeatherForecast> GetWeatherForecast()
    {
        var weatherSummaries = _weatherSummariesService.GetWeatherSummaries();

        if (!weatherSummaries.Any())
            throw new IndexOutOfRangeException();
        
        return Enumerable.Range(1, 5).Select(index => new WeatherForecast
            {
                Date = DateTime.Now.AddDays(index),
                TemperatureC = Random.Shared.Next(-20, 55),
                Summary = weatherSummaries[Random.Shared.Next(weatherSummaries.Length)]
            })
            .ToArray();
    }

Should we receive an empty array from the WeatherSummariesService we’d rather raise an exception than return invalid data to the user. But how do we test this?

Let’s write a unit test to first change the setup of our mock and then specifically listen for an exception:

    [Fact]
    public void GetWeatherForecast_WeatherSummariesEmpty_ThrowsIndexOutOfRangeException()
    {
        // arrange
        _weatherSummariesServiceMock.Setup(x => x.GetWeatherSummaries())
            .Returns(Array.Empty<string>());
        
        // act
        Func<IEnumerable<WeatherForecast>> action = () => _testClass.GetWeatherForecast();
        
        // assert
        action.Should().Throw<IndexOutOfRangeException>();
    }

Let’s go through this step-by-step.

First, we’re changing the setup of our mocked implementation. It’s important to note that Moq will take the latest setup, that’s why we can override our constructor setup with what we define here.

Next, instead of directly executing the method, we keep it as an action (Func<...>), I’ve explicitly shown this instead of a var to make it clearer.

Lastly, we assert that when this action is executed, it does return the appropriate exception type.

Conclusion

It’s important to remember why we unit test and to not get bogged down in testing logic that falls outside of our concern. When you implement a Nuget library in your code you can’t test the logic that lives inside that library, right? Moq makes our tests easier by ensuring we compartmentalise what we’re testing.