Using Mock Frameworks in C# (Moq, NSubstitute)
Learn how to use mock frameworks like Moq and NSubstitute in C# to isolate dependencies and write effective unit tests.
In real-world unit testing, it is essential to isolate the behavior of the class under test from its dependencies.
This is where mock frameworks come into play.
Popular libraries such as Moq and NSubstitute create fake objects (mock objects) that simulate dependencies.
This way, only the behavior of the class being tested is verified.
What Is a Mock?
A mock is a fake object that replaces a class’s real dependencies. During testing, this allows:
- No need for real database or service connections,
- Tests run faster and more isolated,
- External errors (API, network, disk) are avoided,
- Verification of whether specific methods were called.
// Example scenario: A user registration class that depends on an email service
public interface IEmailService
{
void Send(string address, string message);
}
public class UserRegistration
{
private readonly IEmailService _emailService;
public UserRegistration(IEmailService emailService)
{
_emailService = emailService;
}
public void Register(string name)
{
// Registration process...
_emailService.Send("admin@site.com", $"{name} has been successfully registered!");
}
}
When testing this class, we don’t want to send a real email. Instead, we use a mock email service.
What Is the Moq Framework?
Moq is one of the most popular mock frameworks in the .NET ecosystem. It uses a fluent API for readability and provides powerful verification support.
// Install via NuGet:
dotnet add package Moq
using Moq;
using Xunit;
public class UserRegistrationTests
{
[Fact]
public void Register_ShouldSendEmail()
{
// Arrange
var mockMail = new Mock<IEmailService>();
var registration = new UserRegistration(mockMail.Object);
// Act
registration.Register("Ahmet");
// Assert
mockMail.Verify(
m => m.Send("admin@site.com", "Ahmet has been successfully registered!"),
Times.Once); // Method should be called once
}
}
The Verify() method checks whether a specific behavior actually occurred.
This enables behavior-based testing.
Defining Mock Behaviors
With Moq, you can not only verify behaviors but also define return values for mocked methods.
public interface IStockService
{
int Get(string productCode);
}
public class ProductSale
{
private readonly IStockService _stock;
public ProductSale(IStockService stock) => _stock = stock;
public bool Sell(string productCode)
{
int quantity = _stock.Get(productCode);
return quantity > 0;
}
}
public class ProductSaleTests
{
[Fact]
public void Should_Succeed_WhenProductInStock()
{
var mock = new Mock<IStockService>();
mock.Setup(s => s.Get("ABC123")).Returns(10);
var sale = new ProductSale(mock.Object);
bool result = sale.Sell("ABC123");
Assert.True(result);
}
}
The Setup() method defines how the mock object should behave for specific calls.
The Returns() method specifies the return value.
Using Callback
In Moq, the Callback() method adds a side effect when a method is called —
for example, writing a log or incrementing a counter during testing.
int counter = 0;
var mock = new Mock<IEmailService>();
mock.Setup(m => m.Send(It.IsAny<string>(), It.IsAny<string>()))
.Callback(() => counter++);
var registration = new UserRegistration(mock.Object);
registration.Register("Zeynep");
Assert.Equal(1, counter);
In this example, the counter increases every time Send() is called.
This approach is very useful for event tracking in tests.
The It Helper Class
Moq provides the It helper class for parameter checks. This allows you to test only specific parameters or use wildcards.
It.IsAny<T>()– Accepts any value.It.Is<T>(predicate)– Matches values satisfying a condition.It.IsInRange(min, max, Range.Inclusive)– Tests for value ranges.
mock.Verify(m =>
m.Send(It.IsAny<string>(),
It.Is<string>(msg => msg.Contains("Ahmet"))),
Times.Once);
What is the NSubstitute Framework?
NSubstitute provides a simple API for creating mocks, similar to Moq. It is highly readable and naturally supports the “Arrange–Act–Assert” pattern.
// Installation via NuGet:
dotnet add package NSubstitute
using NSubstitute;
using Xunit;
public class UserRegistrationTests
{
[Fact]
public void Register_ShouldSendEmail()
{
var email = Substitute.For<IEmailService>();
var registration = new UserRegistration(email);
registration.Register("Mehmet");
email.Received(1).Send("admin@site.com", "Mehmet was successfully registered!");
}
}
The Received() method checks whether a method call actually occurred.
It provides a more natural, readable syntax for tests.
Defining Behavior (Returns and When)
In NSubstitute, you can define mock behaviors using the Returns() and When() methods.
var stock = Substitute.For<IStockService>();
stock.Get("P001").Returns(5);
var sale = new ProductSale(stock);
bool result = sale.Sell("P001");
Assert.True(result);
// Adding a side effect during a call:
stock.When(s => s.Get("P001"))
.Do(_ => Console.WriteLine("Stock service was called."));
The When(...).Do(...) structure is ideal for defining custom actions when a specific call occurs.
Moq vs NSubstitute Comparison
| Feature | Moq | NSubstitute |
|---|---|---|
| Usage Style | Fluent API (Setup/Verify) | Natural language (Returns/Received) |
| Readability | High | Very high |
| Performance | Slightly faster | Average |
| Callback Support | Yes (.Callback()) | Yes (.Do()) |
| Popularity | Very common (often used with xUnit) | Less common, but modern syntax |
Real-World Example: Order Service Test
The example below shows a scenario where a service dependency is tested using both Moq and NSubstitute.
public interface IOrderService
{
void Save(string product);
}
public class EmailService
{
public virtual void Send(string message)
{
Console.WriteLine($"Email sent: {message}");
}
}
public class OrderProcessor
{
private readonly IOrderService _order;
private readonly EmailService _email;
public OrderProcessor(IOrderService order, EmailService email)
{
_order = order;
_email = email;
}
public void CompleteOrder(string product)
{
_order.Save(product);
_email.Send($"{product} order completed!");
}
}
// Test with Moq
var orderMock = new Mock<IOrderService>();
var emailMock = new Mock<EmailService>();
var process = new OrderProcessor(orderMock.Object, emailMock.Object);
process.CompleteOrder("Laptop");
orderMock.Verify(o => o.Save("Laptop"), Times.Once);
emailMock.Verify(e => e.Send("Laptop order completed!"), Times.Once);
// Test with NSubstitute
var order = Substitute.For<IOrderService>();
var email = Substitute.For<EmailService>();
var process2 = new OrderProcessor(order, email);
process2.CompleteOrder("Laptop");
order.Received(1).Save("Laptop");
email.Received(1).Send("Laptop order completed!");
Performance and Best Practices
- Use mock objects only for external dependencies (database, service, API, file access, etc.).
- When testing business logic, use real classes; mocks should only represent external services.
- Avoid unnecessary verifications — tests should be simple and focused.
- Follow the
Arrange – Act – Assertpattern in unit tests. - Know when to use Fakes or Stubs instead of mocks.
TL;DR
- Mock frameworks create fake objects to isolate dependencies in tests.
- Moq: Uses a Fluent API, widely adopted with strong verification features.
- NSubstitute: Offers a more readable and natural syntax.
Setup()andReturns()→ Define behavior.Verify()/Received()→ Perform call verification.- Mocks focus on testing the target class without interacting with real systems.
Related Articles
Delegates and Events in C#
Learn delegates and events in C# to build event-driven applications using callbacks, subscriptions, and real-world examples.
Dependency Injection Basics in C#
Learn the basics of Dependency Injection in C#, including managing dependencies, loose coupling, and improving testability.
Writing Unit Tests in C# (xUnit, NUnit, MSTest)
Learn how to write unit tests in C# using xUnit, NUnit, and MSTest to build reliable and maintainable applications.