Moq vs NSubstitute: .NET Mocking Libraries Compared
Mocking libraries let you create test doubles — stand-ins for dependencies that your code under test interacts with. In .NET, the two most popular options are Moq and NSubstitute. They solve the same problem but take different approaches to API design. Let's compare them where it counts.
Setting Up a Mock
Consider a service that depends on an IEmailSender interface:
public interface IEmailSender
{
Task SendAsync(string to, string subject, string body);
Task<bool> ValidateAddressAsync(string email);
}
Moq
Moq uses a Mock<T> wrapper class. You set up expectations on it, then extract the .Object property to get the actual interface implementation.
var mock = new Mock<IEmailSender>();
mock.Setup(x => x.ValidateAddressAsync(It.IsAny<string>()))
.ReturnsAsync(true);
var service = new NotificationService(mock.Object);
NSubstitute
NSubstitute creates substitutes directly — no wrapper object needed. The API reads like natural method calls.
var emailSender = Substitute.For<IEmailSender>();
emailSender.ValidateAddressAsync(Arg.Any<string>())
.Returns(true);
var service = new NotificationService(emailSender);
The difference is immediately visible. NSubstitute gives you the interface directly, while Moq requires you to work with a Mock<T> proxy and remember to use .Object when passing it around.
Argument Matching
Both libraries support flexible argument matching, but the syntax differs:
// Moq
mock.Setup(x => x.SendAsync(
It.Is<string>(s => s.Contains("@")),
It.IsAny<string>(),
It.IsNotNull<string>()));
// NSubstitute
emailSender.SendAsync(
Arg.Is<string>(s => s.Contains("@")),
Arg.Any<string>(),
Arg.Is<string>(s => s != null));
Both are equally capable here. NSubstitute uses Arg.Is and Arg.Any rather than Moq's It.Is and It.IsAny. Functionally identical; stylistically a matter of preference.
Verifying Calls
After running the code under test, you often need to verify that certain methods were called.
Moq
[Fact]
public async Task RegisterUser_SendsWelcomeEmail()
{
var mock = new Mock<IEmailSender>();
mock.Setup(x => x.ValidateAddressAsync(It.IsAny<string>()))
.ReturnsAsync(true);
var service = new UserService(mock.Object);
await service.RegisterAsync("[email protected]", "Alice");
mock.Verify(x => x.SendAsync(
"[email protected]",
It.Is<string>(s => s.Contains("Welcome")),
It.IsAny<string>()),
Times.Once);
}
NSubstitute
[Fact]
public async Task RegisterUser_SendsWelcomeEmail()
{
var emailSender = Substitute.For<IEmailSender>();
emailSender.ValidateAddressAsync(Arg.Any<string>())
.Returns(true);
var service = new UserService(emailSender);
await service.RegisterAsync("[email protected]", "Alice");
await emailSender.Received(1).SendAsync(
"[email protected]",
Arg.Is<string>(s => s.Contains("Welcome")),
Arg.Any<string>());
}
NSubstitute's Received() reads more naturally than Moq's Verify() — you call the method on the substitute itself, which feels like reading the interaction you expect. Moq's approach, while functional, is more verbose with its lambda-based verification.
Throwing Exceptions
// Moq
mock.Setup(x => x.SendAsync(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>()))
.ThrowsAsync(new SmtpException("Connection failed"));
// NSubstitute
emailSender.SendAsync(Arg.Any<string>(), Arg.Any<string>(), Arg.Any<string>())
.ThrowsAsync(new SmtpException("Connection failed"));
Returning Different Values Per Call
// Moq - sequential returns
mock.SetupSequence(x => x.ValidateAddressAsync(It.IsAny<string>()))
.ReturnsAsync(true)
.ReturnsAsync(false);
// NSubstitute - sequential returns
emailSender.ValidateAddressAsync(Arg.Any<string>())
.Returns(true, false);
NSubstitute's syntax is more concise here — just pass multiple values to Returns.
The Moq SponsorLink Controversy
In August 2023, Moq version 4.20 included a package called SponsorLink that collected developer email hashes at build time. The community reaction was strongly negative, and SponsorLink was removed in version 4.20.2. While the immediate issue was resolved, the incident led many teams to evaluate alternatives. NSubstitute saw a significant uptick in adoption as a result.
Key Differences
| Feature | Moq | NSubstitute |
|---|---|---|
| API style | Wrapper (Mock<T>) |
Direct substitute |
| Readability | Verbose lambdas | Fluent, natural |
| Strict mode | MockBehavior.Strict |
Substitute.For with Received checks |
| Community trust | Damaged by SponsorLink | Stable |
| Performance | Comparable | Comparable |
Which Should You Choose?
NSubstitute if you're starting a new project. Its API is cleaner, more readable, and requires less ceremony. The library has a strong track record of stability.
Moq if your existing codebase already uses it and migration isn't worth the effort. Moq is still a capable library and the SponsorLink issue has been resolved.
Both libraries ultimately generate the same kind of dynamic proxy objects under the hood. The decision is primarily about developer experience and trust.