DEV Community

Audrius Masiulionis
Audrius Masiulionis

Posted on

A better way to verify Mocks (XUnit, Moq, .NET)

When I started practicing TDD writing unit tests became my everyday routine. Over my professional career, I've picked up a few techniques that help write tests more cleanly and verify all of the code dependencies. I use XUnit test framework for writing tests and Moq Nuget package to mock code dependencies. I've noticed that most developers are not familiar with good mock verification techniques, which I will cover in this post.

In this post, I will not cover the benefits of unit testing like better code quality, executable documentation, or ease of executing complex business scenarios swiftly. I find unit testing in most cases beneficial and even mandatory.

Sample Code

To start unit testing some code is needed. I've created a simple class with a single method. Class's method contains logic to create a new order.

public class OrderService
{
    private readonly IItemRepository _itemRepository;
    private readonly IOrderRepository _orderRepository;

    public OrderService(
        IItemRepository itemRepository, 
        IOrderRepository orderRepository)
    {
        _itemRepository = itemRepository;
        _orderRepository = orderRepository;
    }

    public async Task CreateAsync(int itemId, int itemQuantity)
    {
        var item = await _itemRepository.GetAsync(itemId);
        if (item.Stock < itemQuantity) 
            throw new Exception($"Item id=[{itemId}] has not enough stock for order.");

        Order order = new()
        {
            ItemId = item.Id,
            Quantity = itemQuantity
        };

        await _orderRepository.CreateAsync(order);
    }
}
Enter fullscreen mode Exit fullscreen mode

Let's assume that this retrieves data from a database. Those dependencies are abstracted in repositories. Abstracted dependencies are mandatory for mocking in unit testing.

Testing the code

I like to start with a happy path unit test. But first, let's begin with some test boilerplate code.

public class OrderServiceTests
{
    private readonly OrderService _sut;
    private readonly Mock<IItemRepository> _itemRepositoryMock;
    private readonly Mock<IOrderRepository> _orderRepositoryMock;

    public OrderServiceTests()
    {
        _itemRepositoryMock = new Mock<IItemRepository>();
        _orderRepositoryMock = new Mock<IOrderRepository>();
        _sut = new OrderService(
            _itemRepositoryMock.Object, 
            _orderRepositoryMock.Object);
    }

    [Fact]
    public void CreateAsync_ShouldCreateNewOrder()
    {
        Assert.False(true);
    }
}
Enter fullscreen mode Exit fullscreen mode

If the code is building successfully and we are getting a failed test result we are all set. The next step is to implement the happy path unit test.

[Fact]
    public async Task CreateAsync_ShouldCreateNewOrder()
    {
        const int itemId = 1;
        const int quantity = 2;
        const int existingItemStock = 3;

        _itemRepositoryMock.Setup(m => m.GetAsync(It.Is<int>(i => i == itemId)))
            .ReturnsAsync(() => new Item
            {
                Id = itemId,
                Stock = existingItemStock
            });

        await _sut.CreateAsync(itemId, quantity);

        _itemRepositoryMock.Verify(m => m.GetAsync(itemId));
    }
Enter fullscreen mode Exit fullscreen mode

Let's break down this test. I am using AAA(Arrange, Act, Assert) pattern for all my unit tests. This is an industry-standard and is a clean way to structure tests.

  • In the "arrange" part we specify what parameters are being used and set up the mock's method result after call.
  • In the "act" part we call the method that is being tested.
  • In the "assert" part mock calls are verified that has been set up. I am not using Asserts because my method doesn't return a value.

Although this test passed, it has a problem. I didn't verify if await _orderRepository.CreateAsync(order);. And I didn't setup _orderRepositry mock. This is caused by Moq default mock behavior. When you create a mock the behavior is set to Default. How this is performed can be viewed in Moq's source code:

/// <summary>
///   Initializes an instance of the mock with <see cref="MockBehavior.Default"/> behavior.
/// </summary>
/// <example>
///   <code>
///     var mock = new Mock<IFormatProvider>;();
///   </code>
/// </example>
public Mock(): this(MockBehavior.Default)
{
}
Enter fullscreen mode Exit fullscreen mode

MockBehavior is an enum that specifies your created mocks behavior. Available values and behaviors are:

  • Strict: an exception is thrown whenever a method or property is invoked without matching configuration.
  • Loose: Moq accepts all invocations and attempts to create a valid return value.
  • Default: same as Loose.

By default Moq allows to create unit tests without forcing declaring every expected call. This can speed up test development, but I believe this is a bad practice. Developers must own responsibility for their code and every change they make to the code. This can be ensured by setting MockBehavior strict. This will require to set mock invocations.

 public OrderServiceTests()
    {
        _itemRepositoryMock = new Mock<IItemRepository>(MockBehavior.Strict);
        _orderRepositoryMock = new Mock<IOrderRepository>(MockBehavior.Strict);
        _sut = new OrderService(_itemRepositoryMock.Object, _orderRepositoryMock.Object);
    }
Enter fullscreen mode Exit fullscreen mode

If mock invocation isn't set up exception is trhowned with following message:

Moq.MockException
IOrderRepository.CreateAsync(Order) invocation failed with mock behavior Strict.
All invocations on the mock must have a corresponding setup.
....
Enter fullscreen mode Exit fullscreen mode

To fix the test we need to add the setup for IOrderRepository.

[Fact]
    public async Task CreateAsync_ShouldCreateNewOrder()
    {
        const int itemId = 1;
        const int quantity = 2;
        const int existingItemStock = 3;

        _itemRepositoryMock.Setup(m => m.GetAsync(It.Is<int>(i => i == itemId)))
            .ReturnsAsync(() => new Item
            {
                Id = itemId,
                Stock = existingItemStock
            });

        _orderRepositoryMock.Setup(m => m.CreateAsync(It.IsAny<Order>())).Returns(Task.CompletedTask);

        await _sut.CreateAsync(itemId, quantity);

        _itemRepositoryMock.Verify(m => m.GetAsync(itemId));
        _orderRepositoryMock.Verify(m => m.CreateAsync(It.IsAny<Order>()));
    }
Enter fullscreen mode Exit fullscreen mode

After these fixes you can see that we are introducing more verification code. If you have multiple setups all the verifications must be performed. If your code is more complex and has multiple method calls this introduces complexity and trivial code which can be avoided. This can be achieved with MockRepository class which is helps mock instance creation and management. This class helps to create and verify mocks in one place.

public class OrderServiceTests
{
    private readonly OrderService _sut;
    private readonly MockRepository _mockRepository;
    private readonly Mock<IItemRepository> _itemRepositoryMock;
    private readonly Mock<IOrderRepository> _orderRepositoryMock;

    public OrderServiceTests()
    {
        _mockRepository = new MockRepository(MockBehavior.Strict);
        _itemRepositoryMock = _mockRepository.Create<IItemRepository>();
        _orderRepositoryMock = _mockRepository.Create<IOrderRepository>();
        _sut = new OrderService(_itemRepositoryMock.Object, _orderRepositoryMock.Object);
    }
Enter fullscreen mode Exit fullscreen mode

Using MockRepository we set same mock behavior and we can verify all calls using VerifyAll() method;

 [Fact]
    public async Task CreateAsync_ShouldCreateNewOrder()
    {
        //test code

        //_itemRepositoryMock.Verify(m => m.GetAsync(itemId));
        //_orderRepositoryMock.Verify(m => m.CreateAsync(It.IsAny<Order>()));
        _mockRepository.VerifyAll();
    }
Enter fullscreen mode Exit fullscreen mode

To take a step further we can use Dispose() method to verify mocks. Dispose() using XUnit runs after each test, so we can verify all mocks that have been setup and avoid _mockRepository.VerifyAll() code line in each test method.

public class OrderServiceTests : IDisposable
{
    private readonly OrderService _sut;
    private readonly MockRepository _mockRepository;
    private readonly Mock<IItemRepository> _itemRepositoryMock;
    private readonly Mock<IOrderRepository> _orderRepositoryMock;

    public OrderServiceTests(){...}

    [Fact]
    public async Task CreateAsync_ShouldCreateNewOrder(){...}

    public void Dispose()
    {
        _mockRepository.VerifyAll();
    }
}
Enter fullscreen mode Exit fullscreen mode

This way we have a neat way to verify mocks and keep the test code cleaner.

Full code can be found here.
More on Moq can be found here.

Top comments (1)

Collapse
 
dinhtona profile image
dinhtona

What if:

    public async Task CreateAsync(int itemId, int itemQuantity)
    {
        var item = await _itemRepository.GetAsync(itemId);
        if (item.Stock < itemQuantity) 
            throw new Exception($"Item id=[{itemId}] has not enough stock for order.");

        Order order = new()
        {
            ItemId = item.Id,
            Quantity = itemQuantity
        };

        using(var creatorClass = new SomeCreatorClass())
        {
           /*
            * another setup for creatorClass  class
            */
            creatorClass.CreateAsync(order);
        };
    }
Enter fullscreen mode Exit fullscreen mode

How can I verify this line creatorClass.CreateAsync(order);