Much functionality of Moq is obvious but some of it is not. This article gives an explanation of it and explains how it improves your code and makes production issues less likely.
Introduction
An explanation is given of how Moq is often used and how this way of working can be improved.
Background
It will be really helpful if you have some experience with xUnit, mocking and fixtures in .NET Core. The tests shown here are written for .NET Core but most of the code can be used in situations where another unit testing framework is used. If you are not familiar with fixtures, I recommend reading this article. If you are not familiar with Moq, I recommend reading this article.
Using the Code
We start with code we want to test:
[HttpGet("{queryEntry}", Name = "GetNumberOfCharacters")]
public async Task<ActionResult<int>> GetNumberOfCharacters(string queryEntry)
{
var numberOfCharacters =
await _searchEngineService.GetNumberOfCharactersFromSearchQuery(queryEntry);
return Ok(numberOfCharacters);
}
A unit test can be made easily with AutoFixture.AutoMoq
(NuGet package here). The mock is created with the Freeze
method in the arrange part of the test and verified and the end of the test with the Verify
method. The Create
method is called in between to create the instance that holds the method we want to test. This is how the code looks like:
[Fact]
public async Task GetTest()
{
var fixture = new Fixture().Customize(new AutoMoqCustomization());
var service = fixture.Freeze<Mock<ISearchEngineService>>();
service.Setup(a => a.GetNumberOfCharactersFromSearchQuery(It.IsNotNull<string>()))
.ReturnsAsync(8);
var controller = fixture.Build<SearchEngineController>().OmitAutoProperties().Create();
var response = await controller.GetNumberOfCharacters("Hoi");
Assert.Equal(8, ((OkObjectResult)response.Result).Value);
service.Verify(s => s.GetNumberOfCharactersFromSearchQuery("Hoi"), Times.Once);
}
In addition, we can create an integration test (instead of only a unit test) that just resolves all dependencies as set from the Startup
class and just replaces the single dependency we want to mock. The main advantage is that we get better code coverage (since the Startup
class and Program
class are triggered). The main disadvantage is that we have less isolation so this generally will not replace a unit test. This can be done with IntegrationFixture
(NuGet package here) and this is how the code looks like:
[Fact]
public async Task GetTestFreeze()
{
await using (var fixture = new Fixture<Startup>())
{
var service = fixture.Freeze<Mock<ISearchEngineService>>();
service.Setup(a => a.GetNumberOfCharactersFromSearchQuery(It.IsNotNull<string>()))
.ReturnsAsync(8);
var controller = fixture.Create<SearchEngineController>();
var response = await controller.GetNumberOfCharacters("Hoi");
Assert.Equal(8, ((OkObjectResult) response.Result).Value);
service.Verify(s => s.GetNumberOfCharactersFromSearchQuery("Hoi"), Times.Once);
}
}
This code may look fine but in both tests, a mistake is made. We did not verify if any other calls are done on the mock. This may sound irrelevant but it is not. Other calls can simply cause side effects which is often the main cause of production issues. This is the extra code we need at the end of both tests:
service.VerifyNoOtherCalls();
It is easy to forget this. AutoFixture.AutoMoq
automatically comes with Moq 4.7 which does not support this method. To be able to call it, a more recent version (at least 4.8) needs to be installed separately. Moreover, IntegrationFixture
does not come with any Moq version itself so also for this situation, you need to install it separately. This method really helps preventing side effects but is easy to forget. If you want to see all code: it is on GitHub.
Points of Interest
I learned this method the hard way. I discovered a real production issue caused by a real side effect which I could have discovered before in case I would have used the VerifyNoOtherCalls
method. Hopefully, this article helps you not make the same mistake I made.
History
- 26th May, 2020: Initial version