How to Use Strict Mocking in Moq to Automatically Verify All Setups
Tags: csharp, testing, dotnet, tutorial
Published: 2023-08-23 13:03:00

When using mocks, we often set them up to return values for use in the logic we’re testing. In situations where we have many methods to configure, it’s easy to accidentally forget a setup or two. When this happens, our tests can produce unexpected results and fail. To help narrow down where things go wrong, we can verify the mocks to check that the methods we’ve set up have been called. In previous parts of this series, we’ve seen how this is helpful. However, it can also mean the number of verify statements equals that of the methods set up. In more complex test, we could end up with a lot of code.

This week, we’ll explore the concept of Strict mocks and see how they can help in cases like this.

## Setting the Scene

Let’s imagine we’re writing a system to gather statistics from survey data. To keep things focussed in our example, our data model for a survey participant only has two properties: an ID number, and the participant’s age. We also have a repository with a method to get a participant's age from their ID. And finally, we have a service to calculate the average age of the participants we provide the IDs of.

``` csharp
public interface IParticipantRepository
{
    int GetParticipantAge(int participantId);
}

public class SurveyParticipant
{
    public int Age { get; set; }
    public int Id { get; set; }
}

public class SurveyStatisticsService
{
    private readonly IParticipantRepository
        _participantRepository;

    public SurveyStatisticsService(
        IParticipantRepository participantRepository)
    {
        _participantRepository = participantRepository;
    }

    public double GetAverageAge(IList<int> participantIds)
    {
        double totalAge = 0;

        foreach (var id in participantIds)
        {
            var age = _participantRepository
                .GetParticipantAge(id);

            totalAge += age;
        }

        var average = totalAge / participantIds.Count;
        return average;
    }
}
```

## Writing a Test

With that in place, we want a test to check the logic for calculating the average age of participants. We wrote a few notes with a pen and paper while devising the system and want to encode the data shown in Table 1 into our test.


![Table 1: Our test data. The average age is 28.5](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/h4cam0p5weekozt1jvck.png)

<figcaption>Table 1: Our test data. The average age is 28.5</figcaption>

In the following code, you’ll notice we assert the result should be between 28.499 and 28.501. This is because `GetAverageAge` returns a `double`; comparing floating-point numbers to a specific value can lead to unexpected results due to how they are represented internally, e.g. `1` vs. `1.00000000000001`.

``` csharp
[Test]
public void CanReturnAverageAge()
{
    // Arrange

    var repositoryMock = new Mock<IParticipantRepository>();

    repositoryMock
        .Setup(r => r.GetParticipantAge(0))
        .Returns(27);

    repositoryMock
        .Setup(r => r.GetParticipantAge(1))
        .Returns(32);

    repositoryMock
        .Setup(r => r.GetParticipantAge(3))
        .Returns(29);

    var service = new SurveyStatisticsService(
        repositoryMock.Object);

    // Act

    var result = service.GetAverageAge(
        new[] { 0, 1, 2, 3 });

    // Assert

    Assert.That(result, Is.InRange(28.499, 28.501));
}
```

However, we’ll find the test fails when we run it.

```
Expected: in range (28.499,28.501)
But was:  22.0d
```

Is the logic in `GetAverageAge` wrong?

Or is there a problem in the test?

If you’re particularly keen-eyed, you may have spotted that we forgot to include a setup for participant 2. We saw in previous discussions that mocks will return an empty value of the corresponding data type if a setup hasn’t been provided. An empty value for `double` would be `0`, and while invalid as a participant’s age, it’s perfectly usable in the context of calculating an average.

One way to catch this would be to verify that `GetParticipantAge` was called with all participant IDs in our test data set. However, as we forgot to add a setup, it’s also plausible that we might forget to add the corresponding verification step. To guard against this, we could use a Strict mock.

## Strict Mocks

Mocks can be set up to behave in one of two ways in Moq. The first (and default) is _Loose_, where empty values are provided in the absence of method setups. We’ve seen this before with `null` being returned for reference types, and `0` for `double`.

The alternative behaviour is _Strict_. Instead of returning an empty value, a Strict mock will throw an exception when a method without a corresponding setup is called.

A mock’s behaviour can be specified using a constructor parameter and cannot be changed after instantiation. However, we can check its behaviour by looking at its (read-only) `Behavior` property. The following shows a modified version of our test, where the mock has Strict behaviour.

``` csharp
[Test]
public void CanReturnAverageAge()
{
    // Arrange

    var repositoryMock = new Mock<IParticipantRepository>(
        MockBehavior.Strict);

    repositoryMock
        .Setup(r => r.GetParticipantAge(0))
        .Returns(27);

    repositoryMock
        .Setup(r => r.GetParticipantAge(1))
        .Returns(32);

    repositoryMock
        .Setup(r => r.GetParticipantAge(3))
        .Returns(29);

    var service = new SurveyStatisticsService(
        repositoryMock.Object);

    // Act

    var result = service.GetAverageAge(new[] { 0, 1, 2, 3 });

    // Assert

    Assert.That(result, Is.InRange(28.499, 28.501));
}
```

The test stills fail when we run it. However, we’ll find the error message to be more indicative of where the problem lies.

```
Moq.MockException: 'IParticipantRepository.GetParticipantAge(2)
invocation failed with mock behavior Strict.

All invocations on the mock must have a corresponding setup.'
```

## Summary

By default, Mocks created in Moq return empty values when calling unconfigured methods. Strict mocks can save you time by highlighting missing setup steps when you run your tests. You can set a mock’s behaviour to Strict by passing the corresponding value as a constructor parameter. The mock will then throw an exception every time that a method without an explicit setup is called. Accidental omissions can be caught quickly, and you won’t end up wasting time unnecessarily debugging production logic.

Best of all, you can do this without explicitly writing `Verify` statements for your entire data set, keeping your test code tidy.

---

Thanks for reading!

This article is from my [newsletter](https://webdeveloperdiary.substack.com/?sd=pf). If you found it useful, please consider subscribing. You’ll get more articles like this delivered straight to your inbox (once per week), plus bonus developer tips too!