DEV Community

Cover image for How to Check How Many Times Mocked Methods Are Called in Unit Tests
Anthony Fung
Anthony Fung

Posted on • Originally published at webdeveloperdiary.substack.com

3 1

How to Check How Many Times Mocked Methods Are Called in Unit Tests

In last week’s part, we looked at two ways to check that methods on our Moq testing mocks were called as part of our unit and integration tests.

One way was to set up a callback on our mock to add to a collection, e.g. a List. This can make data easier to work with when writing assertions but means introducing additional variables in the test.

The other involved navigating through a mock’s method-invocation records. While this doesn’t require additional variables, test assertions can become quite complex.

This week, we’ll wrap up by looking at one more option.

How many times did it happen?

Let’s imagine we’re building a new feature for an online shopping platform. We want to send repeat customers (those who have made more than one purchase) an email offering them a loyalty reward. We currently have the following code.

public interface IEmailService
{
    void SendEmail(int customerId);
}

public class Customer
{
    public int Id { get; set; }
    public string Name { get; set; }
    public int TotalPurchases { get; set; }
}

public class CustomerRewardsService
{
    private readonly IEmailService _emailService;

    public CustomerRewardsService(IEmailService emailService)
    {
        _emailService = emailService;
    }

    public void SendEmailToRepeatCustomers(IList<Customer> customers)
    {
        var repeatCustomers = customers.Where(c => c.TotalPurchases > 1);

        foreach (var customer in repeatCustomers)
        {
            _emailService.SendEmail(customer.Id);
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

We also want to check that the email is only sent to qualifying customers. While we could write the test using either of the previously explored techniques, let’s look at one more alternative.

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

    var customers = new List<Customer>
    {
        new Customer { Id = 0, TotalPurchases = 0, Name = "Alice" },
        new Customer { Id = 1, TotalPurchases = 1, Name = "Bob" },
        new Customer { Id = 2, TotalPurchases = 2, Name = "Charlie" },
        new Customer { Id = 3, TotalPurchases = 3, Name = "Dave" }
    };

    var emailService = Mock.Of<IEmailService>();
    var rewardsService = new CustomerRewardsService(emailService);

    // Act

    rewardsService.SendEmailToRepeatCustomers(customers);

    // Assert

    Mock.Get(emailService).Verify(s =>
        s.SendEmail(It.IsAny<int>()), Times.Exactly(2));
}
Enter fullscreen mode Exit fullscreen mode

In the Arrange section, we’ve created a list of customers. Only Charlie and Dave have made more than one purchase. As such, we want to make sure (only) two emails are sent while processing the list. We do this in the Assert phase by using Times.Exactly(2) when verifying the mock. As we’re using It.IsAny<int>(), we’re only checking that SendEmail is called twice; we don’t pay attention to the customer ID. To ensure the email is only sent to Charlie and Dave (customers 2 and 3 respectively), we can add the following additional verification statements.

Mock.Get(emailService).Verify(s =>
    s.SendEmail(2), Times.Once);

Mock.Get(emailService).Verify(s =>
    s.SendEmail(3), Times.Once);
Enter fullscreen mode Exit fullscreen mode

Here, we’re using Times.Once to verify that exactly one email is sent to each qualifying customer – not only would sending more than one be spamming, but it would also unnecessarily spend our email credits.

We’ve used exact counts in these examples. If our verification requirements were less strict, we also have other options such as:

  • Times.AtLeast

  • Times.Between

  • Times.AtMost

What if it Never Happened?

So far, our test checks for expected behaviour: one email is sent to Charlie, and one to Dave. However, sometimes it’s just as important (if not more) to check that something doesn’t happen. If we want to explicitly check that no emails are sent to Alice or Bob, we can use the Times.Never or Times.Exactly(0) constraints.

Mock.Get(emailService).Verify(s =>
    s.SendEmail(0), Times.Exactly(0));

Mock.Get(emailService).Verify(s =>
    s.SendEmail(1), Times.Never);
Enter fullscreen mode Exit fullscreen mode

Summary

Sometimes you need to know how many times a method has been called on testing mocks. With Moq, you can verify both exact numbers (including 0), and ranges.

While you could have achieved this using the previously explored approaches, each had its own advantages and disadvantages. If neither feels suitable, specifying the number of times a method is called as part of mock verification might be the best way forward.


Thanks for reading!

This article is from my newsletter. If you found it useful, please consider subscribing. You’ll get more articles like this delivered straight to your inbox (once per week), plus bonus developer tips too!

Reinvent your career. Join DEV.

It takes one minute and is worth it for your career.

Get started

Top comments (0)

A Workflow Copilot. Tailored to You.

Pieces.app image

Our desktop app, with its intelligent copilot, streamlines coding by generating snippets, extracting code from screenshots, and accelerating problem-solving.

Read the docs

👋 Kindness is contagious

Explore a sea of insights with this enlightening post, highly esteemed within the nurturing DEV Community. Coders of all stripes are invited to participate and contribute to our shared knowledge.

Expressing gratitude with a simple "thank you" can make a big impact. Leave your thanks in the comments!

On DEV, exchanging ideas smooths our way and strengthens our community bonds. Found this useful? A quick note of thanks to the author can mean a lot.

Okay