🏠 VisualStudioTutor.com  ·  C# Tutorial Home  ·  C# Lesson 24 of 40
Lesson 24 of 40 Testing Advanced ⏱ 35 min

Unit Testing with xUnit 3 & NSubstitute

Test business logic in isolation with xUnit and replace external collaborators with NSubstitute test doubles.

Part 1: What You Will Learn

Test business logic in isolation with xUnit and replace external collaborators with NSubstitute test doubles.

  • Write a focused test with `[Fact]`.
  • Substitute an interface rather than calling a real external service.
  • Assert the returned result.
  • Verify that a collaborator received the expected call.

Project setup: Create an xUnit test project. Add the `NSubstitute` package and reference the project that contains the production classes.

Part 2: Topic-Specific Working Example

The following example is written specifically for this lesson. Create the project described above, enter the code, run it, and then change some values to observe how the feature behaves.

using NSubstitute;
using Xunit;

public interface IEmailSender
{
    void Send(string address, string message);
}

public sealed class RegistrationService(IEmailSender emailSender)
{
    public bool Register(string email)
    {
        if (string.IsNullOrWhiteSpace(email))
            return false;

        emailSender.Send(email, "Welcome!");
        return true;
    }
}

public sealed class RegistrationServiceTests
{
    [Fact]
    public void Register_ValidEmail_SendsWelcomeMessage()
    {
        // Arrange
        IEmailSender sender = Substitute.For<IEmailSender>();
        RegistrationService service = new(sender);

        // Act
        bool result = service.Register("student@example.com");

        // Assert
        Assert.True(result);
        sender.Received(1)
              .Send("student@example.com", "Welcome!");
    }
}

Part 3: How the Code Works

  • The test uses Arrange, Act, Assert so its intention is easy to follow.
  • `Substitute.For<IEmailSender>()` creates a controllable fake implementation.
  • `Assert.True(result)` checks the return value.
  • `Received(1)` verifies the side effect without sending a real email.

Part 4: Mini Project & Practice

Mini project: add a second test named `Register_EmptyEmail_ReturnsFalseAndDoesNotSend`. Use `DidNotReceive()` to verify that no email was sent.

Tip: Type the code yourself in Visual Studio 2026, run it, then deliberately change one part at a time. The goal is to understand the feature rather than simply copy the finished example.

When you are comfortable with this lesson, continue to Lesson 25.

C# in Visual Studio 2026

📘 This lesson is part of the book C# in Visual Studio 2026 by Dr. Liew Voon Kiong.

View on Amazon Kindle Edition