Moq
unit testing
mocking framework
C# development
method invocation

Using Moq to determine if a method is called

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

Moq verification is a standard way to confirm that one unit called a dependency method with expected arguments. Correct verification helps ensure behavior, not only output, especially in services that coordinate repositories, queues, or external clients. The key is to verify meaningful interactions and avoid brittle over-verification.

Basic Verify Pattern

Create a mock, execute the method under test, and assert invocation count.

csharp
1using Moq;
2using Xunit;
3
4public interface IEmailSender {
5    void Send(string to, string body);
6}
7
8public class NotificationService {
9    private readonly IEmailSender _emailSender;
10
11    public NotificationService(IEmailSender emailSender) {
12        _emailSender = emailSender;
13    }
14
15    public void NotifyWelcome(string email) {
16        _emailSender.Send(email, "Welcome");
17    }
18}
19
20public class NotificationServiceTests {
21    [Fact]
22    public void NotifyWelcome_CallsSendOnce() {
23        var emailMock = new Mock<IEmailSender>();
24        var sut = new NotificationService(emailMock.Object);
25
26        sut.NotifyWelcome("[email protected]");
27
28        emailMock.Verify(m => m.Send("[email protected]", "Welcome"), Times.Once);
29    }
30}

This verifies that the interaction happened exactly once with expected values.

Verify with Flexible Argument Matching

When exact body text is not important, use argument matchers.

csharp
1emailMock.Verify(
2    m => m.Send(
3        It.Is<string>(x => x.EndsWith("@example.com")),
4        It.Is<string>(body => body.Contains("Welcome"))
5    ),
6    Times.Once
7);

This keeps tests robust when incidental formatting changes.

Verify No Unexpected Calls

VerifyNoOtherCalls helps catch accidental side effects.

csharp
emailMock.Verify(m => m.Send(It.IsAny<string>(), It.IsAny<string>()), Times.Once);
emailMock.VerifyNoOtherCalls();

Use this selectively. In highly collaborative services, strict no-other-calls checks can become brittle during legitimate refactoring.

Asynchronous Methods

For async dependencies, verify invocation after awaiting the SUT method.

csharp
1using System.Threading.Tasks;
2
3public interface IAuditClient {
4    Task WriteAsync(string message);
5}
6
7public class AuditService {
8    private readonly IAuditClient _client;
9
10    public AuditService(IAuditClient client) {
11        _client = client;
12    }
13
14    public async Task TrackAsync(string item) {
15        await _client.WriteAsync($"tracked:{item}");
16    }
17}
18
19[Fact]
20public async Task TrackAsync_CallsWriteAsync() {
21    var client = new Mock<IAuditClient>();
22    client.Setup(c => c.WriteAsync(It.IsAny<string>())).Returns(Task.CompletedTask);
23    var sut = new AuditService(client.Object);
24
25    await sut.TrackAsync("x1");
26
27    client.Verify(c => c.WriteAsync("tracked:x1"), Times.Once);
28}

Always await async methods to avoid false negatives caused by race timing.

Design Better Verifications

Verify interactions that represent business behavior, not implementation trivia. For example, verifying repository save call is useful; verifying every logger call often is not.

Good tests usually combine:

  • State assertion where applicable.
  • One or two interaction assertions on critical collaborators.
  • Minimal coupling to internal method order.

Setup and Callback Techniques

Sometimes you need to capture invocation arguments for deeper assertions. Moq callbacks make this straightforward.

csharp
1string? capturedTo = null;
2string? capturedBody = null;
3
4emailMock
5    .Setup(m => m.Send(It.IsAny<string>(), It.IsAny<string>()))
6    .Callback<string, string>((to, body) => {
7        capturedTo = to;
8        capturedBody = body;
9    });
10
11sut.NotifyWelcome("[email protected]");
12
13Assert.Equal("[email protected]", capturedTo);
14Assert.Contains("Welcome", capturedBody);

This complements Verify when you need richer assertions than simple call count.

Ordered Interaction Verification

If order matters across dependencies, use explicit sequencing patterns. Keep this only for workflows where order is part of business behavior.

csharp
1var repo = new Mock<IRepository>();
2var bus = new Mock<IEventBus>();
3
4var sequence = new MockSequence();
5repo.InSequence(sequence).Setup(r => r.Save(It.IsAny<string>()));
6bus.InSequence(sequence).Setup(b => b.Publish(It.IsAny<string>()));

Overusing order assertions makes tests fragile, so reserve them for critical flows.

Strict Versus Loose Mocks

Moq defaults to loose behavior. Strict mocks throw on unconfigured calls.

csharp
var strictMock = new Mock<IEmailSender>(MockBehavior.Strict);
strictMock.Setup(m => m.Send("[email protected]", "Welcome"));

Strict mode can catch accidental calls early, but it increases maintenance cost. Choose based on team preference and test scope.

Practical Test Design Guidance

A strong interaction test usually has one responsibility. Keep arrange data small, execute one action, and assert only essential interactions. This keeps failure messages clear and reduces false positives during refactors.

Common Pitfalls

  • Verifying too many internal calls and making tests fragile.
  • Forgetting to await async methods before verification.
  • Using overly broad It.IsAny and missing argument bugs.
  • Asserting exact call counts when behavior is intentionally idempotent.
  • Treating interaction verification as replacement for result assertions.

Summary

  • Use Verify to assert critical dependency interactions.
  • Match arguments precisely where behavior depends on them.
  • Use VerifyNoOtherCalls carefully for strict boundaries.
  • Await async flows before asserting invocation.
  • Keep verifications behavior-focused, not implementation-heavy.

Course illustration
Course illustration

All Rights Reserved.