.Net application development specialists
asp.net, c#, vb.net, html, javascript, jquery, html, xhtml, css, oop, design patterns, sql server, mvc and much more
contact: admin@paxium.co.uk

Paxium is the company owned by myself, Dave Amour and used for providing IT contract development services including


  • Application development - Desktop, Web, Services - with Classic ASP, Asp.net WebForms, Asp.net MVC, Asp.net Core
  • Html, Css, JavaScript, jQuery, React, C#, SQL Server, Ado.net, Entity Framework, NHibernate, TDD, WebApi, GIT, IIS
  • Database schema design, implementation & ETL activities
  • Website design and hosting including email hosting
  • Training - typically one to one sessions
  • Reverse Engineering and documentation of undocumented systems
  • Code Reviews
  • Performance Tuning
  • Located in Cannock, Staffordshire
Rugeley Chess Club Buying Butler Cuckooland Katmaid Pet Sitting Services Roland Garros 60 60 Golf cement Technical Conformity Goofy MaggieBears Vacc Track Find Your Smart Phone eBate Taylors Poultry Services Lafarge Rebates System Codemasters Grid Game eBate DOFF

Why We Mock in the First Place

For extra info see https://docs.educationsmediagroup.com/unit-testing-csharp/moq/verifications#implicit-or-explicit-verification

Unit testing is about testing a single piece of code in isolation. When your class depends on external services, databases, APIs, or other components, you face a dilemma: how do you test your code without dragging all those dependencies along? This is where mocking comes in.

Mocking allows you to create fake implementations of your dependencies that behave exactly how you want them to for your test. Instead of connecting to a real database or calling an actual email service, you create a mock that returns predetermined values or simulates specific behaviors. This is crucial because during testing, you don't want to actually send emails to real customers, delete records from production databases, charge credit cards, or make API calls that consume quotas or cost money. Mocks let you simulate these operations safely without any real-world side effects.

This gives you several crucial benefits: your tests run fast because they're not waiting on external systems, they're reliable because they're not affected by network issues or database states, they're safe because they don't cause real-world consequences, and they're focused solely on testing your code's logic rather than the integration with external systems.

But mocking isn't just about controlling what your dependencies return—it's also about verifying that your code interacts with those dependencies correctly. This is where Moq's verification methods become essential.

London vs Chicago School of Testing

Having said the above, there are some schools of thought that prefer not to mock. So the testing community has two distinct philosophies about how to approach mocking and verification, often referred to as the London and Chicago schools of testing. These schools differ fundamentally in what they believe tests should verify.

The London school (also called "mockist" testing) emphasizes testing behavior and interactions. Tests following this approach mock most or all dependencies and use extensive verification to ensure the class under test calls the right methods with the right parameters. The focus is on how your code communicates with its collaborators. If your method should save to a repository and send an email, a London-style test verifies that both the save and email methods were called, regardless of what they return.

The Chicago school (also called "classical" or "Detroit" school) takes a different approach, preferring to test state over interactions. Tests in this style use real objects wherever practical and only mock at architectural boundaries like databases or external APIs. Verification is used sparingly, primarily to check interactions that don't produce observable state changes. The focus is on what the system produces, not how it produces it.

Both approaches have merit. London-style testing produces highly focused unit tests that clearly document expected interactions, but can become brittle when implementation details change. Chicago-style testing produces more robust tests that survive refactoring, but can be less clear about which component caused a failure. Understanding both philosophies helps you make informed decisions about when to verify interactions and when to trust state-based assertions. Moq's verification methods are essential tools for London-style testing, and selective tools for Chicago-style testing.

Why Chicago School Testing Excels with CPU-Bound Dependencies

CPU-bound dependencies are ideal candidates for Chicago-style testing because they possess three critical characteristics: they're fast, deterministic, and side-effect free. A price calculator that multiplies quantities by unit prices runs in microseconds and always produces the same result for the same inputs. There's simply no compelling reason to mock it. When you use the real implementation, your tests verify that the actual calculation logic is correct, not just that a method was called. If there's a bug in how discounts compound with taxes, you'll catch it immediately because the final order total will be wrong.

Mocking CPU-bound dependencies actually makes tests worse. You end up hard-coding expected values in your mock setups that should be calculated by the real code, duplicating logic and creating maintenance burdens. If business rules change—say, tax calculations need to account for product categories—you have to update both the production code and every mock setup in your tests. With Chicago-style testing using real implementations, you only change the calculator itself and verify the new behavior through state assertions. Your tests become documentation of what the system actually does, not what methods it calls along the way. The only time you should mock a computational dependency is when it's genuinely slow (like complex machine learning inference) or non-deterministic (like random number generation), and even then, you should question whether that dependency is well-designed.

The Purpose of Verification

When you write a unit test, you typically follow the Arrange-Act-Assert pattern. In the Arrange phase, you set up your mocks to return specific values. In the Act phase, you call the method you're testing. But in the Assert phase, you need to verify two things: first, that your method returned the correct result, and second, that it interacted with its dependencies in the expected way.

This second type of assertion is what Moq's verification methods handle. Did your code call the database save method? Did it log an error message when something went wrong? Did it check user permissions before performing an operation? These are behavioral assertions about how your code uses its dependencies, and they're just as important as assertions about return values.

Choosing the Right Verification Approach

Moq provides several different ways to verify mock interactions, each suited to different scenarios. The Verify() method allows you to check that a specific method was called with particular parameters. The VerifyGet() and VerifySet() methods handle property access verification. The VerifyAll() method checks that all setups marked as verifiable were actually invoked. And verification can happen either explicitly in your assertions or implicitly through strict mocks that fail automatically if unexpected methods are called.

Understanding when to use each verification approach is crucial for writing tests that are both thorough and maintainable. Using Verify() gives you explicit, readable assertions about individual interactions. Using VerifyAll() can make tests more concise when you're checking multiple related calls. And understanding the trade-offs between loose and strict mocks helps you balance test brittleness against catching unexpected behaviors.

In this article, we'll explore each of Moq's verification methods in detail, examining their syntax, use cases, and best practices. We'll look at real-world examples that demonstrate when each approach shines and when it becomes a hindrance. By the end, you'll have a comprehensive understanding of how to verify mock interactions effectively, making your unit tests more robust and your code more reliable.

Case Study Code

For the example tests we shall examine in this article we need a class to test and some dependencies that the class uses.

I have created some sample code for this. It is contrived and not real-world code and written in such a way to hit many different methods and properties so we can explore verification options. The code is an Azure Function and looks like this:

using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.Functions.Worker;
using Microsoft.Extensions.Logging;
using MoqExamples.Lib;
using System.Text.Json;

namespace MoqExamples.Function;

public class CustomerFunction(ILogger<CustomerFunction> logger, ICustomerService customerService)
{
    [Function("CustomerFunction")]
    public async Task<IActionResult> Run([HttpTrigger(AuthorizationLevel.Anonymous, "post")] HttpRequest req)
    {
        logger.LogError(customerService.MinimumLogLevel == "Error"
            ? $"The customer service minimum log level is {customerService.MinimumLogLevel} which may not be ideal"
            : $"The customer service minimum log level is {customerService.MinimumLogLevel}");

        Customer? customer;

        try
        {
            customer = await JsonSerializer.DeserializeAsync<Customer>(req.Body, new JsonSerializerOptions
            {
                PropertyNameCaseInsensitive = true
            });
        }
        catch (Exception)
        {
            logger.LogError("Invalid customer supplied");
            
            return new BadRequestObjectResult(new ProblemDetails
            {
                Status = StatusCodes.Status400BadRequest,
                Title = "Invalid Request Body",
                Detail = "The request body could not be parsed as a valid Customer object.",
                Instance = req.Path,
                Type = "https://tools.ietf.org/html/rfc7231#section-6.5.1"
            });
        }

        if (!customerService.IsValidCustomer(customer!))
        {
            return new BadRequestObjectResult(new ProblemDetails
            {
                Status = StatusCodes.Status400BadRequest,
                Title = "Invalid Customer Data",
                Detail = "The customer data failed validation. Ensure FirstName, LastName, and at least one contact method (Email or PhoneNumber) are provided.",
                Instance = req.Path,
                Type = "https://tools.ietf.org/html/rfc7231#section-6.5.1"
            });
        }

        logger.LogInformation($"Request for CustomerId {customer} received");

        var customers = customerService.GetAllCustomers();

        //Silly logic but just for demonstration
        if (customers.Any(c => c.FirstName == customer!.FirstName && c.LastName == customer.LastName))
        {
            return new ConflictObjectResult(new ProblemDetails
            {
                Status = StatusCodes.Status409Conflict,
                Title = "Customer Already Exists",
                Detail = "A customer with the same details already exists.",
                Instance = req.Path,
                Type = "https://tools.ietf.org/html/rfc7231#section-6.5.8"
            });
        }

        var newCustomer = customerService.Add(customer!);

        return new OkObjectResult(newCustomer);
    }
}

We will use the following mocks for its dependencies

private readonly Mock<ILogger<CustomerFunction>> _mockLogger = new();
private readonly Mock<ICustomerService> _mockCustomerService = new ();
private readonly Mock<HttpRequest> _mockHttpRequest = new();

Before we dive in we first need to look at how we will verify log calls. If we try and do something like this

using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Moq;
using MoqExamples.Lib;
using Xunit;

namespace MoqExamples.Function.Tests.cs
{
    public class CustomerFunctionTests
    {
        private readonly Mock<ILogger<CustomerFunction>> _mockLogger = new();
        private readonly Mock<ICustomerService> _mockCustomerService = new();
        private readonly Mock<HttpRequest> _mockHttpRequest = new();

        [Fact]
        public void WhenCustomerFunctionRunAndCustomerServiceMinimumLogLevelIsErrorCorrectLogMessageIsGenerated_UsingVerify()
        {
            var function = new CustomerFunction(_mockLogger.Object, _mockCustomerService.Object);

            _mockCustomerService.SetupGet(m => m.MinimumLogLevel).Returns("Error");

            _ = function.Run(_mockHttpRequest.Object);

            _mockLogger.Verify(m => m.LogInformation(
                "The customer service minimum log level is Error which may not be ideal"));
        }
    }
}

Then we get the following error

Log Error

Which Moq isn't able to deal with. So we have to write a helper class that looks as below and helps use to verify and setup logger calls

using Microsoft.Extensions.Logging;
using Moq;
using System.Diagnostics.CodeAnalysis;

namespace MoqExamples.Function.Tests.cs
{
    [ExcludeFromCodeCoverage]
    public static class LogVerifier
    {
        public static void VerifyLogInformationCalled<T>(Mock<ILogger<T>> logger, string message)
        {
            logger.Verify(
                l => l.Log(
                    LogLevel.Information,
                    It.IsAny<EventId>(),
                    It.Is<It.IsAnyType>((v, t) => v.ToString()!.Contains(message)),
                    It.IsAny<Exception>(),
                    ((Func<It.IsAnyType, Exception, string>)It.IsAny<object>())!), Times.Once);
        }

        public static void VerifyLogWarningCalled<T>(Mock<ILogger<T>> logger, string message)
        {
            logger.Verify(
                l => l.Log(
                    LogLevel.Warning,
                    It.IsAny<EventId>(),
                    It.Is<It.IsAnyType>((v, t) => v.ToString()!.Contains(message)),
                    It.IsAny<Exception>(),
                    ((Func<It.IsAnyType, Exception, string>)It.IsAny<object>())!), Times.Once);
        }

        public static void VerifyErrorCalled<T>(Mock<ILogger<T>> logger, string message)
        {
            logger.Verify(
                l => l.Log(
                    LogLevel.Error,
                    It.IsAny<EventId>(),
                    It.Is<It.IsAnyType>((v, t) => v.ToString()!.Contains(message)),
                    It.IsAny<Exception>(),
                    ((Func<It.IsAnyType, Exception, string>)It.IsAny<object>())!), Times.Once);
        }

        // Setup methods with Verifiable
        public static void SetupLogInformationAsVerifiable<T>(Mock<ILogger<T>> logger, string message)
        {
            logger.Setup(
                    l => l.Log(
                        LogLevel.Information,
                        It.IsAny<EventId>(),
                        It.Is<It.IsAnyType>((v, t) => v.ToString()!.Contains(message)),
                        It.IsAny<Exception>(),
                        ((Func<It.IsAnyType, Exception, string>)It.IsAny<object>())!))
                .Verifiable();
        }

        public static void SetupLogWarningAsVerifiable<T>(Mock<ILogger<T>> logger, string message)
        {
            logger.Setup(
                    l => l.Log(
                        LogLevel.Warning,
                        It.IsAny<EventId>(),
                        It.Is<It.IsAnyType>((v, t) => v.ToString()!.Contains(message)),
                        It.IsAny<Exception>(),
                        ((Func<It.IsAnyType, Exception, string>)It.IsAny<object>())!))
                .Verifiable();
        }

        public static void SetupLogErrorAsVerifiable<T>(Mock<ILogger<T>> logger, string message)
        {
            logger.Setup(
                    l => l.Log(
                        LogLevel.Error,
                        It.IsAny<EventId>(),
                        It.Is<It.IsAnyType>((v, t) => v.ToString()!.Contains(message)),
                        It.IsAny<Exception>(),
                        ((Func<It.IsAnyType, Exception, string>)It.IsAny<object>())!))
                .Verifiable();
        }
    }
}

And we can then use this in our tests like this

using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Moq;
using MoqExamples.Lib;
using Xunit;

namespace MoqExamples.Function.Tests.cs
{
    public class CustomerFunctionTests
    {
        private readonly Mock<ILogger<CustomerFunction>> _mockLogger = new();
        private readonly Mock<ICustomerService> _mockCustomerService = new ();
        private readonly Mock<HttpRequest> _mockHttpRequest = new();

        [Fact]
        public void WhenCustomerFunctionRunAndCustomerServiceMinimumLogLevelIsErrorCorrectLogMessageIsGenerated_UsingVerify()
        {
            //Arrange
            var function = new CustomerFunction(_mockLogger.Object, _mockCustomerService.Object);

            _mockCustomerService.SetupGet(m => m.MinimumLogLevel).Returns("Error");

            //Act
            _ = function.Run(_mockHttpRequest.Object);

            //Assert
            LogVerifier.VerifyErrorCalled(_mockLogger, "The customer service minimum log level is Error which may not be ideal");
        }
    }
}

So when you see something like

    
    LogVerifier.VerifyErrorCalled(_mockLogger, "The customer service minimum log level is Error which may not be ideal");
    

You can consider it equivalent to in spirit at least

    
     _mockLogger.Verify(m => m.LogInformation("The customer service minimum log level is Error which may not be ideal"));
    

So the first unit test above is our first scenario. The part of the code we are testing is this

    
[Function("CustomerFunction")]
public async Task<IActionResult> Run([HttpTrigger(AuthorizationLevel.Anonymous, "post")] HttpRequest req)
{
    logger.LogError(customerService.MinimumLogLevel == "Error"
        ? $"The customer service minimum log level is {customerService.MinimumLogLevel} which may not be ideal"
        : $"The customer service minimum log level is {customerService.MinimumLogLevel}");
        
    

We check the log level of the customerService and then log one of two messages to the function logger based on this. Remember this is silly code and just used to create samples we can test!

For this test there is nothing for us to set up with the function logger itself. The function log method does not return anything (ie returns void) so we don't and can't tell it to return anything in particular. We are only interested in whether the function logger called LogError and that it did so with the correct message. We do need to tell the mock customer service MinimumLogLevel property to return "Error" and we do that as follows


//Arrange
var function = new CustomerFunction(_mockLogger.Object, _mockCustomerService.Object);

_mockCustomerService.SetupGet(m => m.MinimumLogLevel).Returns("Error");

Having done that we can then test that the function logger calls LogError with the correct message. We can then write a similar test for when the MinimumLogLevel is not Error. That test could look something like this


    using Microsoft.AspNetCore.Http;
    using Microsoft.Extensions.Logging;
    using Moq;
    using MoqExamples.Lib;
    using Xunit;

    namespace MoqExamples.Function.Tests.cs
    {
        public class CustomerFunctionTests
        {
            private readonly Mock<ILogger<CustomerFunction>> _mockLogger = new();
            private readonly Mock<ICustomerService> _mockCustomerService = new ();
            private readonly Mock<HttpRequest> _mockHttpRequest = new();

            [Fact]
            public void WhenCustomerFunctionRunAndCustomerServiceMinimumLogLevelIsErrorCorrectLogMessageIsGenerated_UsingVerify()
            {
                //Arrange
                var function = new CustomerFunction(_mockLogger.Object, _mockCustomerService.Object);

                _mockCustomerService.SetupGet(m => m.MinimumLogLevel).Returns("Information");

                //Act
                _ = function.Run(_mockHttpRequest.Object);

                //Assert
                LogVerifier.VerifyErrorCalled(_mockLogger, "The customer service minimum log level is Information");
            }
        }
    }

Now suppose we want to setup multiple moq setups on a bunch of calls something like below

_mockCustomerService.SetupGet(m => m.MinimumLogLevel).Returns("Info");

_mockCustomerService.Setup(m => m.IsValidCustomer(It.Is<Customer>(c => c.FirstName == "John"))).Returns(true);

_mockCustomerService.Setup(m => m.GetAllCustomers()).Returns(existingCustomers);

_mockCustomerService.Setup(m => m.Add(It.Is<Customer>(c => c.FirstName == "John"))).Returns(newCustomer);

So we have setup these calls whilst we could to some extent infer that they were in fact called that way by the result being as expected, we could also explicitly check they were called as in the test below

[Fact]
public async Task WhenValidNewCustomerProvidedAllServiceMethodsAreCalled_UsingNormalVerify()
{
    //Arrange
    var function = new CustomerFunction(_mockLogger.Object, _mockCustomerService.Object);

    var customerJson = """{"firstName":"John","lastName":"Doe","email":"john@example.com"}""";

    var stream = new MemoryStream(Encoding.UTF8.GetBytes(customerJson));
    
    _mockHttpRequest.Setup(r => r.Body).Returns(stream);

    var newCustomer = new Customer { Id = Guid.NewGuid(), FirstName = "John", LastName = "Doe", Email = "john@example.com" };
    
    var existingCustomers = new List<Customer>
    {
        new() { Id = Guid.NewGuid(), FirstName = "Jane", LastName = "Smith", Email = "jane@example.com" }
    };

    _mockCustomerService.SetupGet(m => m.MinimumLogLevel).Returns("Info");

    _mockCustomerService.Setup(m => m.IsValidCustomer(It.Is<Customer>(c => c.FirstName == "John"))).Returns(true);

    _mockCustomerService.Setup(m => m.GetAllCustomers()).Returns(existingCustomers);

    _mockCustomerService.Setup(m => m.Add(It.Is<Customer>(c => c.FirstName == "John"))).Returns(newCustomer);

    //Act
    var result = await function.Run(_mockHttpRequest.Object);

    //Assert
    Assert.IsType<OkObjectResult>(result);

    _mockCustomerService.Verify(m => m.MinimumLogLevel);

    _mockCustomerService.Verify(m => m.IsValidCustomer(It.Is<Customer>(c => c.FirstName == "John")));

    _mockCustomerService.Verify(m => m.GetAllCustomers());

    _mockCustomerService.Verify(m => m.Add(It.Is<Customer>(c => c.FirstName == "John")));
}

There is an easier way of achieving this though using a moq method called Verify All as below

    
_mockCustomerService.VerifyAll();
        

And this simply checks that all the setups we did on the mock customer service were in fact called that way. In order for this to happen though you have to mark your setups as Verifiable as below

_mockCustomerService.SetupGet(m => m.MinimumLogLevel).Returns("Info").Verifiable();

_mockCustomerService.Setup(m => m.IsValidCustomer(It.Is<Customer>(c => c.FirstName == "John"))).Returns(true).Verifiable();

_mockCustomerService.Setup(m => m.GetAllCustomers()).Returns(existingCustomers).Verifiable();

_mockCustomerService.Setup(m => m.Add(It.Is<Customer>(c => c.FirstName == "John"))).Returns(newCustomer).Verifiable();

And this would leave the full test looking something like this

[Fact]
public async Task WhenValidNewCustomerProvidedAllServiceMethodsAreCalled_UsingNormalVerify()
{
    //Arrange
    var function = new CustomerFunction(_mockLogger.Object, _mockCustomerService.Object);

    var customerJson = """{"firstName":"John","lastName":"Doe","email":"john@example.com"}""";

    var stream = new MemoryStream(Encoding.UTF8.GetBytes(customerJson));
    
    _mockHttpRequest.Setup(r => r.Body).Returns(stream);

    var newCustomer = new Customer { Id = Guid.NewGuid(), FirstName = "John", LastName = "Doe", Email = "john@example.com" };
    
    var existingCustomers = new List<Customer>
    {
        new() { Id = Guid.NewGuid(), FirstName = "Jane", LastName = "Smith", Email = "jane@example.com" }
    };

    _mockCustomerService.SetupGet(m => m.MinimumLogLevel).Returns("Info").Verifiable();

    _mockCustomerService.Setup(m => m.IsValidCustomer(It.Is<Customer>(c => c.FirstName == "John"))).Returns(true).Verifiable();

    _mockCustomerService.Setup(m => m.GetAllCustomers()).Returns(existingCustomers).Verifiable();

    _mockCustomerService.Setup(m => m.Add(It.Is<Customer>(c => c.FirstName == "John"))).Returns(newCustomer).Verifiable();

    //Act
    var result = await function.Run(_mockHttpRequest.Object);

    //Assert
    Assert.IsType<OkObjectResult>(result);

    _mockCustomerService.VerifyAll();
}

Whilst this is less code to write, there are some advantages to using individual Verity calls. You can check how many times something was called for example, or even that it wasn't called at all as below

We should also now note that if we want to do something similar with our LogVerifier then we do have Verifiable setups as below

using Microsoft.Extensions.Logging;
using Moq;
using System.Diagnostics.CodeAnalysis;

namespace MoqExamples.Function.Tests.cs
{
    [ExcludeFromCodeCoverage]
    public static class LogVerifier
    {
        public static void VerifyLogInformationCalled<T>(Mock<ILogger<T>> logger, string message)
        {
            logger.Verify(
                l => l.Log(
                    LogLevel.Information,
                    It.IsAny<EventId>(),
                    It.Is<It.IsAnyType>((v, t) => v.ToString()!.Contains(message)),
                    It.IsAny<Exception>(),
                    ((Func<It.IsAnyType, Exception, string>)It.IsAny<object>())!), Times.Once);
        }

        public static void VerifyLogWarningCalled<T>(Mock<ILogger<T>> logger, string message)
        {
            logger.Verify(
                l => l.Log(
                    LogLevel.Warning,
                    It.IsAny<EventId>(),
                    It.Is<It.IsAnyType>((v, t) => v.ToString()!.Contains(message)),
                    It.IsAny<Exception>(),
                    ((Func<It.IsAnyType, Exception, string>)It.IsAny<object>())!), Times.Once);
        }

        public static void VerifyErrorCalled<T>(Mock<ILogger<T>> logger, string message)
        {
            logger.Verify(
                l => l.Log(
                    LogLevel.Error,
                    It.IsAny<EventId>(),
                    It.Is<It.IsAnyType>((v, t) => v.ToString()!.Contains(message)),
                    It.IsAny<Exception>(),
                    ((Func<It.IsAnyType, Exception, string>)It.IsAny<object>())!), Times.Once);
        }

        // Setup methods with Verifiable
        public static void SetupLogInformationAsVerifiable<T>(Mock<ILogger<T>> logger, string message)
        {
            logger.Setup(
                    l => l.Log(
                        LogLevel.Information,
                        It.IsAny<EventId>(),
                        It.Is<It.IsAnyType>((v, t) => v.ToString()!.Contains(message)),
                        It.IsAny<Exception>(),
                        ((Func<It.IsAnyType, Exception, string>)It.IsAny<object>())!))
                .Verifiable();
        }

        public static void SetupLogWarningAsVerifiable<T>(Mock<ILogger<T>> logger, string message)
        {
            logger.Setup(
                    l => l.Log(
                        LogLevel.Warning,
                        It.IsAny<EventId>(),
                        It.Is<It.IsAnyType>((v, t) => v.ToString()!.Contains(message)),
                        It.IsAny<Exception>(),
                        ((Func<It.IsAnyType, Exception, string>)It.IsAny<object>())!))
                .Verifiable();
        }

        public static void SetupLogErrorAsVerifiable<T>(Mock<ILogger<T>> logger, string message)
        {
            logger.Setup(
                    l => l.Log(
                        LogLevel.Error,
                        It.IsAny<EventId>(),
                        It.Is<It.IsAnyType>((v, t) => v.ToString()!.Contains(message)),
                        It.IsAny<Exception>(),
                        ((Func<It.IsAnyType, Exception, string>)It.IsAny<object>())!))
                .Verifiable();
        }
    }
}

Moq's VerifyAll(): The Void Method Problem

Now we have looked at Moq's VerifyAll() method, we have hit this frustrating issue: it requires Setup() calls for everything, including void methods like loggers. This feels clunky when you wouldn't normally set up these methods just to verify they were called.

Why Does It Work This Way?

  • VerifyAll() is designed to verify that all configured expectations were met—it's about completeness of what you explicitly set up
  • For void methods, Moq distinguishes between "I don't care if this is called" vs "I expect this to be called"

Better Approaches

1. Use Verify() Instead of VerifyAll()

Just verify the specific calls you care about:

mockLogger.Verify(x => x.LogError(It.IsAny<string>()), Times.Once);

2. Use Verifiable() with Verify()

Set up only what matters:

mockLogger.Setup(x => x.LogError(It.IsAny<string>())).Verifiable();
// ... test code ...
mockLogger.Verify(); // Verifies only Verifiable() setups

3. Consider If You Need to Verify Logging at All

Often logging is an implementation detail. You might only verify it in specific scenarios where the log message is critical business behavior.

4. Use Strict Mocks

If you truly want to verify everything was called as expected:

var mockLogger = new Mock<ILogger>(MockBehavior.Strict);
mockLogger.Setup(x => x.LogError(It.IsAny<string>()));
Recommendation: Skip VerifyAll() and use targeted Verify() calls only for behavior that matters to your test. For most logging scenarios, you probably only care about specific error conditions being logged, not every debug statement.

Using a MockRepository

When we create a mock we normally do so like this

private readonly Mock<ILogger<CustomerFunction>> _mockLogger = new();
private readonly Mock<ICustomerService> _mockCustomerService = new ();
private readonly Mock<HttpRequest> _mockHttpRequest = new();

However, it is possible to create them a different way. If we create something called a mock repository first as below

    
var repository = new MockRepository(MockBehavior.Loose);
        

Then we can create our mocks like this

    
var logger = repository.Create<ILogger<CustomerFunction>>();
var customerService = repository.Create<ICustomerService>();
var httpRequest = repository.Create<HttpRequest>();
        

In doing this our mock repository knows about all of our mocks, and then we do have the option to do this

    
repository.VerifyAll();   // Verifies all verifiable setups across *every* mock created by this repository
        

Which might not seem like a great win but if our test contains 5 mocks, and we want to VerifyAll then we can do it in 1 line rather than 5. So not earth-shattering, just slightly cleaner and gives you less chance I suppose of missing one out by mistake.

Note also when we created our mock repository, it took a parameter of MockBehaviour.Loose. Another option for this is MockBehaviour.Strict. We shall examine the differences next.

Moq MockRepository: Loose vs Strict

In Moq, MockRepository controls how your mocks behave — particularly whether they’re strict or loose by default.

Let’s break down what happens when you use:


🧩 new MockRepository(MockBehavior.Loose)

  • Loose mocks don’t throw exceptions if you call a method or property that hasn’t been explicitly set up.
  • Instead, they:
    • Return default values (null, 0, false, or a new mock if DefaultValue.Mock is configured).
    • Allow your code under test to keep running even if not all dependencies are configured.

Use when:

  • You only care about verifying specific interactions (not every call).
  • You want your test to focus on one aspect without needing to stub every method.
  • You're exploring or prototyping tests.
var repository = new MockRepository(MockBehavior.Loose);
var mock = repository.Create<ICustomerService>();

// Calling an unsetup method is fine — no exception.
var result = mock.Object.GetCustomerById(Guid.NewGuid()); // returns null

🔒 new MockRepository(MockBehavior.Strict)

  • Strict mocks throw an exception if your code calls a method or property that you haven’t explicitly set up.
  • This enforces that your test knows exactly which calls happen — useful for tight behavioral verification.

🚫 Example of failure:

var repository = new MockRepository(MockBehavior.Strict);
var mock = repository.Create<ICustomerService>();

// Throws Moq.MockException — this call was not set up!
mock.Object.GetCustomerById(Guid.NewGuid());

Use when:

  • You want to ensure that only expected calls occur.
  • You're testing a small, well-defined unit with predictable interactions.
  • You want to catch accidental or unexpected dependencies.

⚖️ Summary Table

Behavior Unsetup Calls Default Returns Use Case
Loose Allowed Default(T) or new mock Flexible, common default
Strict Throws exception N/A Tight control, detect unwanted calls

💡 Best Practice

Many developers prefer:

var repository = new MockRepository(MockBehavior.Strict);

…when validating core business logic (because it reveals accidental dependencies), and:

var repository = new MockRepository(MockBehavior.Loose);

…for broader integration-style unit tests where flexibility is fine.


Would you like me to show you a short example test using both — so you can see how the behavior differs in practice?

Example: Comparing Loose vs Strict MockRepository in Unit Tests

The following example demonstrates how MockBehavior.Loose and MockBehavior.Strict behave differently in practice. It uses a simple service and a test class with xUnit and Moq.

🧱 Example Interface

public interface ICustomerService
{
    string GetCustomerName(Guid customerId);
}

🧪 Example Class Under Test

public class CustomerProcessor
{
    private readonly ICustomerService _customerService;

    public CustomerProcessor(ICustomerService customerService)
    {
        _customerService = customerService;
    }

    public string GetGreeting(Guid id)
    {
        var name = _customerService.GetCustomerName(id);
        return name is null ? "Hello, Guest!" : $"Hello, {name}!";
    }
}

✅ Loose Mock Example (No Setup Needed)

[Fact]
public void WhenUsingLooseMock_UnsetupCallsReturnDefaults_NoExceptionThrown()
{
    var repository = new MockRepository(MockBehavior.Loose);
    var mockService = repository.Create<ICustomerService>();

    var processor = new CustomerProcessor(mockService.Object);
    var result = processor.GetGreeting(Guid.NewGuid());

    Assert.Equal("Hello, Guest!", result);
}

❌ Strict Mock Example (Unsetup Call Throws)

[Fact]
public void WhenUsingStrictMock_UnsetupCallsThrow_MockExceptionOccurs()
{
    var repository = new MockRepository(MockBehavior.Strict);
    var mockService = repository.Create<ICustomerService>();

    var processor = new CustomerProcessor(mockService.Object);

    // This will throw Moq.MockException because GetCustomerName was never set up.
    Assert.Throws<MockException>(() => processor.GetGreeting(Guid.NewGuid()));
}

🎯 Strict Mock with Setup (Now Passes)

[Fact]
public void WhenUsingStrictMockAndSetupDefined_ExpectedCallSucceeds()
{
    var repository = new MockRepository(MockBehavior.Strict);
    var mockService = repository.Create<ICustomerService>();

    mockService.Setup(s => s.GetCustomerName(It.IsAny<Guid>())).Returns("Ava");

    var processor = new CustomerProcessor(mockService.Object);
    var result = processor.GetGreeting(Guid.NewGuid());

    Assert.Equal("Hello, Ava!", result);
    mockService.VerifyAll(); // Confirms expected calls occurred
}

👉 This clearly shows the difference:

  • Loose mocks let unconfigured calls pass quietly with default values.
  • Strict mocks enforce explicit setups and will throw if something unexpected happens.

Does MockBehavior.Strict vs MockBehavior.Loose Affect VerifyAll()?

Excellent question — and yes, MockBehavior.Strict and MockBehavior.Loose do subtly affect how VerifyAll() behaves, but not in the way many assume.


🧩 VerifyAll() — What It Actually Does

VerifyAll() checks that every setup you’ve made on your mocks was actually called during the test.

mock.Setup(x => x.DoSomething());
...
mock.VerifyAll(); // ✅ passes if DoSomething() was called, ❌ fails otherwise

It doesn’t care whether your mocks are Strict or Loose — it only looks at setups.


🔒 Strict vs 🧩 Loose — Behavioural Differences Around VerifyAll()

Aspect MockBehavior.Loose MockBehavior.Strict
Unsetup calls Allowed (return default values) Throw MockException immediately
VerifyAll() checks Only setups — unaffected by loose behaviour Only setups — still unaffected
Extra calls not setup Ignored (no errors) Fail immediately when they occur
Missed setup calls (VerifyAll) Fail at VerifyAll() time Fail at VerifyAll() time (same rule)

So:

  • VerifyAll() does not depend on strict vs loose.
  • What changes is when failures occur:
    • In Strict mode, you’ll get an exception immediately if you make an unsetup call.
    • In Loose mode, unsetup calls are fine — only missing setup calls fail when you reach VerifyAll().

🧠 Example

Strict Mode

var repo = new MockRepository(MockBehavior.Strict);
var mock = repo.Create<IMyService>();

mock.Setup(x => x.DoSomething());
mock.Object.DoSomething(); // OK
mock.Object.DoSomethingElse(); // ❌ throws immediately in Strict mode

mock.VerifyAll(); // Checks that DoSomething() was called (passes)

Loose Mode

var repo = new MockRepository(MockBehavior.Loose);
var mock = repo.Create<IMyService>();

mock.Setup(x => x.DoSomething());
mock.Object.DoSomethingElse(); // ✅ allowed (returns default)
mock.VerifyAll(); // ❌ fails because DoSomething() was never called

✅ Summary

Behavior Unsetup Calls VerifyAll Effect
Loose Allowed, ignored Only fails if setup not called
Strict Throws immediately Still only checks setups, but extra calls fail earlier

Implicit or explicit verification?

Moq supporting two different styles of verification makes room for making unit tests either unnecessarily tight or dangerously open. The agency offered by Moq consolidates in two approaches:

  • Open configuration, close verification
  • Close configuration, open verification

When following the first approach, developers should configure methods and properties relying as much as possible on constructs like It.IsAny. In the Assert section of the unit test, developers should then rely on explicit verification with constraints as specified as possible.


mock.Setup(p => p.Send(It.IsAny<string>()));
mock.Verify(p => p.Send("Hello world"), Times.Once());

On the other hand, when following the second approach, developers should configure methods and properties specifying as much as possible the expected parameters and rely on implicit verification.


mock.Setup(p => p.Send("Hello world")).Verifiable();
mock.VerifyAll();

Please note that the two approaches are not exactly equivalent as implicit verification does not support checking the amount of invocations.

As mentioned above, using the remaining two approaches (open/open and close/close) is suboptimal when not dangerous. The open/open approach below makes the verification almost useless.


mock.Setup(p => p.Send(It.IsAny<string>())).Verifiable();
mock.VerifyAll();

The close/close approach below makes writing the unit test too cumbersome for no real gain.


mock.Setup(p => p.Send("Hello world"));
mock.Verify(p => p.Send("Hello world"), Times.Once());