Practical ASP.NET Core Unit Testing with xUnit, MOQ, and FluentAssertions

Introduction

Unit testing is a crucial aspect of modern software development that ensures individual components of your codebase function correctly in isolation. When it comes to ASP.NET Core, there are several tools and libraries available to streamline the unit testing process. In this article, we will delve into how to perform unit testing using three popular tools: xUnit, MOQ, and FluentAssertions. Additionally, we will walk through a practical example to illustrate the concepts discussed.

Setting Up Your ASP.NET Core Project

Before starting with unit testing, you must have an ASP.NET Core project. You can create a new project using CLI with the following command.

dotnet new web -n MyWebApp

After setting up your project, go to the project folder by navigating to it.

cd MyWebApp

Installing the Required Packages

To perform unit testing with xUnit, MOQ, and FluentAssertions, you can install the necessary NuGet packages using the CLI or Visual Studio NuGet Package Manager.

dotnet add package xunit
dotnet add package xunit.runner.visualstudio
dotnet add package Moq
dotnet add package FluentAssertions

Writing Your First Test

Let's say you have a simple service class in your ASP.NET Core application that you want to test. Here's an example of such a class.

public class CalculatorService
{
    public int Add(int a, int b)
    {
        return a + b;
    }
}

Now, you want to write a unit test for the Add method. Create a new folder in your project called Tests to organize your test files. Inside the Tests folder, create a new class called CalculatorServiceTests.

using Xunit;
using Moq;
using FluentAssertions;

namespace MyWebApp.Tests
{
    public class CalculatorServiceTests
    {
        [Fact]
        public void Add_Should_Return_Sum_Of_Two_Numbers()
        {
            // Arrange
            var calculatorService = new CalculatorService();

            // Act
            var result = calculatorService.Add(2, 3);

            // Assert
            result.Should().Be(5); // Use FluentAssertions for readable assertions
        }
    }
}

In this test class, we've used xUnit's [Fact] attribute to mark our test method and FluentAssertions to make our assertions more readable. We've arranged the necessary objects, performed the action, and asserted the expected outcome.

Mocking Dependencies with MOQ

In real-world applications, your service classes often depend on other components or external services. To isolate the code you're testing, you can use MOQ to create mock objects for these dependencies.

Let's assume that your CalculatorService class depends on a Logger service.

public interface ILogger
{
    void Log(string message);
}

public class CalculatorService
{
    private readonly ILogger _logger;

    public CalculatorService(ILogger logger)
    {
        _logger = logger;
    }

    public int Add(int a, int b)
    {
        _logger.Log($"Adding {a} and {b}"); // Log the operation
        return a + b;
    }
}

To test the Add method without invoking the real logger, you can create a mock ILogger using MOQ in your test.

using Xunit;
using Moq;
using FluentAssertions;

namespace MyWebApp.Tests
{
    public class CalculatorServiceTests
    {
        [Fact]
        public void Add_Should_Return_Sum_Of_Two_Numbers()
        {
            // Arrange
            var loggerMock = new Mock<ILogger>();
            var calculatorService = new CalculatorService(loggerMock.Object);

            // Act
            var result = calculatorService.Add(2, 3);

            // Assert
            result.Should().Be(5);
        }
    }
}

In this example, we create a mock ILogger using the new Mock<ILogger>() and pass it as a dependency to the CalculatorService. This allows us to test the Add method without invoking the real logger.

Running Unit Tests

To run your unit tests, you can use the dotnet test command from the command line.

dotnet test

This command will discover and execute all the tests in your project, including the ones you've written using xUnit.

Conclusion

Unit testing is a crucial aspect of building reliable and maintainable ASP.NET Core applications. The guide explores the use of xUnit, MOQ, and FluentAssertions to perform unit testing. By following these practices and tools, you can ensure that individual components of your codebase function as expected, making it easier to identify and resolve issues early in the development process.

It is important to remember that unit testing is only one part of the testing process. In addition to unit tests, it is recommended to write integration tests and end-to-end tests to cover different aspects of your application's functionality. Effective testing helps in building robust and high-quality ASP.NET Core applications.