Better Moq expressions with NUnit constraints

2 minute read

The problem

When writing unit tests for components consuming dependencies, it’s important to make sure the right calls were invoked on the dependencies.

Moq has built-in support for tracking invocations: it leverages the same argument matching syntax used for configuring methods to filter invocations.

In Moq’s lingo, this process if often referred to as verification.

When verifying the calls performed on the mock, developers can set expectations on the parameters by using a syntax like in the snippet below.

mock.Verify(p => p.DoSomething(It.Is<int>(s => s > 100)));

The snippet above tells Moq to verify that the DoSomething method of the mock was invoked with a parameter whose value is greater than 100. If this condition is not met, the Verify method will throw an exception so that the unit test fails.

Since the It.Is<T> method accepts a Expression<Func<T, bool>>, instead of a simple Func<T, bool>, there are limitations on what can be set as an expectation for the incoming parameter.

NUnit constraints

If Moq expectations are simple predicate expressions, NUnit has a powerful asserting engine based on constraints that gives developers to write assertions like the ones below.

Assert.That(result, Is.EqualTo(42))
Assert.That(result, Is.GreaterThan(0))
Assert.That(result, Is.Not.Null)
Assert.That(result, Is.False)

A constraint is a class implementing the IConstraint interface and is responsible for applying their own validation logic to the value received from NUnit.

In the snippet above, convenience classes and methods are used to create a fluent syntax that makes assertions read like human languages.

Below are the same assertions written using the actual constraint classes.

Assert.That(result, new EqualConstraint(42));
Assert.That(result, new GreaterThanConstraint(0));
Assert.That(result, new NotConstraint(new NullConstraint()));
Assert.That(result, new FalseConstraint());

Ideally, we want to combine Moq’s It.Is<T> with NUnit’s IConstraint ecosystem.

Custom matchers to the rescue

We can achieve this with the help of a custom matcher.

Custom matchers are a feature offered by Moq to workaround the limitations of the expressions consumed by It.Is<T>.

These custom matchers can be used to encapsulate complex validating logic. They are therefore the perfect place to inject a NUnit constraint to be evaluated when verifying invocations on a mock.

Let’s write, for sake of example, a custom matcher that uses the NUnit GreaterThanConstraint.

public static class Matchers
{
    public static int GreaterThan(int value)
    {
        var constraint = new GreaterThanConstraint(value);
        return Moq.Match.Create<int>(item => constraint.ApplyTo(item).IsSuccess);
    }
}

The matcher above allows us to rewrite the first snippet as follows:

mock.Verify(p => p.DoSomething(Matchers.GreaterThan(100)));

Now, we can generalize the method by accepting the constraint as a parameter and making the method generic so that it can work with every parameter type.

public static T That<T>(IConstraint constraint)
{
    return Moq.Match.Create<T>(item => constraint.ApplyTo(item).IsSuccess);
}

This allows use to use any constraint when validating an invocation on a mock’s method.

mock.Verify(p => p.DoSomething(Matchers.That<int>(Is.GreaterThan(100))));

Reuse

Since I started using this pattern, I noticed that I was polluting many projects with the same small snippet of code. So much for DRY-ness.

To avoid this, I refined this approach and created a NuGet package that can be easily consumed from all test projects using Moq and NUnit.

Here is an example of what can be written when using the InsightArchitectures.Testing.MoqMatchers.NUnit package.

mock.Verify(p => p.DoSomething(Parameter.Is<int>().That(Is.GreaterThan(100))));

As you can notice, the syntax differs a bit from the one presented in this post.

The package is released with a MIT licenese and the source code is available on GitHub.

Recap

Being based on expressions and not delegates, Moq verifications sometimes feel difficult to work with.

By leveraging Moq’s custom matcher feature, we can use NUnit’s powerful constraint ecosystem and even reuse our own constraints.

The NuGet package InsightArchitectures.Testing.MoqMatchers.NUnit contains everything needed.