DEV Community

Cover image for Moq Techniques: How to Pick the Best Syntax when Configuring Your Mocks
Anthony Fung
Anthony Fung

Posted on • Originally published at webdeveloperdiary.substack.com

Moq Techniques: How to Pick the Best Syntax when Configuring Your Mocks

We briefly looked at creating testing mocks in an earlier part of this series. The focus at the time was on the concept of mocking, and to not get side-tracked, we used one of the simplest ways to create and set up our mock. But when we come to test more complex real-world logic, we may want additional configuration options. This week, we’ll look at the two syntax options we can use to set up mocks with Moq, along with their pros and cons.

A Modern Approach to Declaring and Setting Up Mocks

When we previously introduced mocking, we created mocks using LINQ to Mocks. This (relatively) new syntax should be fairly intuitive if you’re used to writing LINQ queries using Method Syntax, i.e. with extension methods like First, Single, and Where. To refresh our memories, here’s how we declared our mock.

var multiplicationService = Mock.Of<IMultiplicationService>(s =>
    s.Square(3) == 9 &&
    s.Square(4) == 16);
Enter fullscreen mode Exit fullscreen mode

When using LINQ to Mocks, the return value (from Mock.Of) is the mocked entity. In this example, this is an IMultiplicationService that’s ready to be used as a method argument or set as a property value.

A Fluent Alternative to LINQ to Mocks

We can also create and set up mocks using a fluent syntax. To achieve the same result as in our previous LINQ to Mocks example, we can use the following code.

var multiplicationService = new Mock<IMultiplicationService>();

multiplicationService
    .Setup(s => s.Square(3))
    .Returns(9);

multiplicationService
    .Setup(s => s.Square(4))
    .Returns(16);
Enter fullscreen mode Exit fullscreen mode

In this case, multiplicationService is an object representing the mock, rather than the mocked entity. We can easily get an IMultiplicationService to use in our tests from the mock’s Object property. Here, this would be multiplicationService.Object.

While more verbose than LINQ to Mocks, it’s still easy to see what’s going on. But that still leaves one question: why would we want to write more code to achieve the same result?

To better explain the answer, let’s first explore another concept related to setting up mocks.

When Arguments Just Don’t Matter

In the previous examples, the argument for our mocked method mattered; the return value was dependent on the provided data:

9 was returned when Square was called with 3.

16 was returned when Square was called with 4.

In some test contexts, a mock’s method arguments are irrelevant – we just need it to return something to use in the test. We can use It.IsAny<T>() in these situations, specifying a generic type that matches the parameter type. For example, imagine we have the following code.

public class MyObject
{
    public int Id { get; set; }
}

public interface IObjectRepository
{
    MyObject GetObject(int id);
}
Enter fullscreen mode Exit fullscreen mode

Suppose we have a test where an IObjectRepository should return a MyObject regardless of the ID passed to it. We can set up a mock to do this with the following code:

var repository = Mock.Of<IObjectRepository>(r =>
    r.GetObject(It.IsAny<int>()) == new MyObject());
Enter fullscreen mode Exit fullscreen mode

We use It.IsAny<int>() in place of an actual integer while setting up the mock. The results in an IObjectRepository that always returns the specified MyObject regardless of the value of id.

Advantages of the Fluent Syntax

Returning to our previous question: why might we choose to write more code for the same result? The answer is that the results are subtly different, and we have greater control with the fluent syntax. In the following test, we retrieve a MyObject twice. We then check that they refer to different object instances.

[Test]
public void ReturnedObjectIsNewInstance()
{
    // Arrange

    var repository = Mock.Of<IObjectRepository>(r =>
        r.GetObject(It.IsAny<int>()) == new MyObject());

    // Act

    var obj1 = repository.GetObject(1);
    var obj2 = repository.GetObject(2);

    // Assert

    Assert.IsTrue(obj1 != obj2);
}
Enter fullscreen mode Exit fullscreen mode

It might come as a surprise that this test fails:

Expected: True
But was:  False
Enter fullscreen mode Exit fullscreen mode

Calling a method mocked with LINQ to Mocks returns the same value on successive invocations. While testing a mock isn’t something we’d do in a real project, the result is relevant if the logic in a test calls a mocked method more than once. If it’s important for a mock to return a new instance per invocation, we might consider using the fluent syntax:

[Test]
public void ReturnedObjectIsNewInstance()
{
    // Arrange

    var repositoryMock = new Mock<IObjectRepository>();

    repositoryMock
        .Setup(r => r.GetObject(It.IsAny<int>()))
        .Returns<int>(i => new MyObject());

    var repository = repositoryMock.Object;

    // Act

    var obj1 = repository.GetObject(1);
    var obj2 = repository.GetObject(2);

    // Assert

    Assert.IsTrue(obj1 != obj2);
}
Enter fullscreen mode Exit fullscreen mode

Running this test passes.

Summary

Moq lets you set up mocks with one of two syntaxes, each with advantages. LINQ to Mocks is modern, concise, and familiar. The fluent style offers more control.

When using LINQ to Mocks, you can easily declare and set up a mock with code similar to writing a LINQ query using Method Syntax. While succinct, the resulting mocks might not be suitable depending on your test – methods configured with return values will return the same instance on every invocation. If this is a problem, consider using the fluent syntax – while more verbose, your mocks will be much more flexible.

Writing less code often makes things simpler. But simplicity cannot be a substitute for correctness. By understanding your options when setting up your mocks, you can make the best choice for the task at hand.


Thanks for reading!

Software development evolves quickly. But we can all help each other learn. By sharing, we all grow together.

I write stories, tips, and insights as an Angular/C# developer.

If you’d like to never miss an issue, you’re invited to subscribe for free for more articles like this, delivered straight to your inbox (link goes to Substack).

Top comments (0)