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);
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);
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);
}
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());
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);
}
It might come as a surprise that this test fails:
Expected: True
But was: False
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);
}
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)