Loading...

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:


// 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.


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

FeatureMoqNSubstitute
Usage StyleFluent API (Setup/Verify)Natural language (Returns/Received)
ReadabilityHighVery high
PerformanceSlightly fasterAverage
Callback SupportYes (.Callback())Yes (.Do())
PopularityVery 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


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() and Returns() → Define behavior.
  • Verify() / Received() → Perform call verification.
  • Mocks focus on testing the target class without interacting with real systems.

Related Articles