How to Check a Method was Called on a Mock in Moq
Tags: csharp, testing, dotnet, tutorial
Published: 2023-08-02 13:22:00

We often use mocks in unit tests. They make it easy to isolate the code modules we’re testing. We often set them up to perform functionally: for a given input – whether a specific value, or any value – they return a value to be used elsewhere. In these conditions, we implicitly know the mocked method has been called. Afterall, if its return value is important, its absence would likely cause the test to fail.

However, not every mock needs to return a value. For example, we might be mocking a repository with a `void` method that would normally write data to a database. As no return values are involved, the mock likewise wouldn’t (and shouldn’t) return anything.

In this part of the series, we’ll look at how we can check that important methods (like the one previously mentioned) are called when our tests run.

## Recreating the Problem and Revisiting a Solution

Let’s assume we have the following code:

``` csharp
public interface IDataRepository
{
    public void Save(string data);
}

public class DataService
{
    private readonly IDataRepository _repository;

    public DataService(IDataRepository repository)
    {
        _repository = repository;
    }

    public void SaveData(string data)
    {
        _repository.Save(data);
    }
}
```

We want to make sure `SaveData` calls `_repository.Save(data)`. We’ve previously written a similar test where we took the approach of adding a callback to `Save`; it added any data passed to it to a `List` collection:

``` csharp
[Test]
public void SaveDataCallsSaveOnRepository()
{
    // Arrange

    var repository = new Mock<IDataRepository>();
    var savedData = new List<string>();

    repository
        .Setup(r => r.Save(It.IsAny<string>()))
        .Callback<string>(data => savedData.Add(data));

    var service = new DataService(repository.Object);

    // Act

    service.SaveData("Some data");

    // Assert

    Assert.That(savedData.Single(), Is.EqualTo("Some data"));
}
```

This will get the job done, but we need to declare an extra `List` variable to store the saved data. If you’d prefer to not introduce new variables, we can do this check another way.

## The Batteries Included Approach

Mocks created with Moq keep records of methods that were called, and the arguments passed on each invocation. In the following example, we use a mock’s `Verify` method to check that `Save` was called with the argument `"Some data"`.

``` csharp
[Test]
public void SaveDataCallsSaveOnRepository()
{
    // Arrange

    var repository = new Mock<IDataRepository>();
    var service = new DataService(repository.Object);

    // Act

    service.SaveData("Some data");

    // Assert

    repository.Verify(r => r.Save("Some data"));
}
```

Note that `Verify` is on the mock; not the mocked entity – an `IDataRepository` in this case. You might remember that using LINQ to Mocks to create mocks returns the mocked entity. In these cases, we can still verify it by getting a reference to the mock with `Mock.get()`, as shown in the following example.

``` csharp
[Test]
public void SaveDataCallsSaveOnRepository()
{
    // Arrange

    var repository = Mock.Of<IDataRepository>();
    var service = new DataService(repository);

    // Act

    service.SaveData("Some data");

    // Assert

    Mock.Get(repository).Verify(r => r.Save("Some data"));
}
```

### We Don’t Care for Arguments…

Sometimes it’s only important that a mocked method was called. If we aren’t concerned with the arguments passed while doing so, we can use `It.IsAny()` – just like setting up a mock.

``` csharp
[Test]
public void SaveDataCallsSaveOnRepository()
{
    // Arrange

    var repository = new Mock<IDataRepository>();
    var service = new DataService(repository.Object);

    // Act

    service.SaveData("Some data");

    // Assert

    repository.Verify(r => r.Save(It.IsAny<string>()));
}
```

### …Unless We Do

Let’s imagine we need to call SaveData twice:

``` csharp
service.SaveData("First save");
service.SaveData("Second save");
```

When we previously had a callback that added the arguments to a `List`, we could verify the data by checking the contents and their order in the `List`. While it’s not possible to do this using `Verify`, we can inspect the mock’s method invocations. Note the four assertions at the end of the following test:

``` csharp
[Test]
public void SaveDataCallsSaveOnRepository()
{
    // Arrange

    var repository = new Mock<IDataRepository>();
    var service = new DataService(repository.Object);

    // Act

    service.SaveData("First save");
    service.SaveData("Second save");

    // Assert

    repository.Verify(r => r.Save(It.IsAny<string>()));

    Assert.That(repository.Invocations[0].Method.Name,
        Is.EqualTo("Save"));

    Assert.That(repository.Invocations[0].Arguments[0],
        Is.EqualTo("First save"));

    Assert.That(repository.Invocations[1].Method.Name,
        Is.EqualTo("Save"));

    Assert.That(repository.Invocations[1].Arguments[0],
        Is.EqualTo("Second save"));
}
```

We first check that `Save` is the name of the first method called on our mock. Then we check that it was called with the argument `"First save"`. The next two assertions do the same thing but for the second invocation:

* Check that the method invoked was named `Save`, and…

* It was called with the argument `"Second save"`.

## Summary

When using testing mocks, you sometimes need to be sure mocked methods are called. You can do this with a separate collection, or by inspecting the mock.

You can use `Verify` to do this quickly and easily, specifying any required arguments at the same time. When a method is called more than once, one disadvantage of this approach is you can’t see the order of the arguments with respect to the method’s successive invocations. If this is important, you can access this information through the mock’s `Invocations` property.

Depending on the requirements of your tests, you may find one approach too complex or not complex enough. But by having knowledge of both, you can choose the best one for the task at hand.

---

Thanks for reading!

This article is from my [newsletter](https://webdeveloperdiary.substack.com/?sd=pf). If you found it useful, please consider subscribing. You’ll get more articles like this delivered straight to your inbox, plus bonus developer tips too!