Better assertions when verifying Moq’d methods have been called

Man wearing fake glasses and moustache

Moq, according to its GitHub page, is “The most popular and friendly mocking framework for .NET”.
Mocking frameworks allow you to create a ‘fake’ version of a dependency (typically an interface, or abstract class).
With this fake, you can then instruct the methods return values, or invoke behaviour.
Alternatively you can assert the methods were called, with a certain set of parameters.
How we verify these methods were called can affect the maintainability of our tests.

Let’s suppose we have this interface.

Which is a dependency of the class we’re testing:

Our method takes a firstName and lastName and creates a `Customer` object using those parameters.
It then passes that `Customer` object to the `Save` method on the injected ICustomerService.
For the purposes of our test, we inject a mocked `ICustomerService`
The `Save` method returns an int, so we set our mocked method to return 1, when it is called with any `Customer` object.

Our unit test could look something like this:

This test will of course pass. But, if we make a small change in our `CustomerController`

Now our test will correctly fail.
However the exact reason for the failure isn’t clear:

Moq.MockException :
Expected invocation on the mock at least once, but was never performed:  
x => x.Save(It.Is(c => c.FirstName == "Alex" && c.LastName == "Brown"))

All this tells us is that we didn’t call our dependency with an object that matches. It doesn’t give any indication as to why that object didn’t match.

Moq has a `Callback` function which we can use to assign our mocked `ICustomerService.Save` method to a variable outside of our setup.
Later on, we can use this variable for assertions.

And now, we can individually assert on each parameter:

If we run this test (without fixing our code from the previous failure) it now gives a much clearer failure:

Expected string length 5 but was 4. Strings differ at index 0.
Expected: "Brown"
But was: "Alex"
-----------^