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
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());