#csharp #dotnet #backend

Day 29: Unit Testing APIs with xUnit & Moq

Welcome to Day 29. Automated testing is the bedrock of professional engineering. If you refactor your OrderService, how do you know you didn’t break anything? You test it.

In .NET, the primary testing framework is xUnit.

Step 1: The Testing Project

Tests should live in a separate project so you don’t ship your test code into production binaries.

# 1. Create the API Project
dotnet new webapi -n MyApi
# 2. Create the Test Project
dotnet new xunit -n MyApi.Tests
# 3. Tell the Test Project about the API Project!
cd MyApi.Tests
dotnet add reference ../MyApi/MyApi.csproj

Step 2: The Logic

Assume we have a simple service in our API Project.

public interface ICalculator { int Add(int a, int b); }
public class CalculatorService : ICalculator 
{ 
    public int Add(int a, int b) => a + b; 
}

Step 3: The xUnit Test

Inside the MyApi.Tests project, write a standard class and method decorated with the [Fact] attribute. The test framework automatically finds and runs it.

public class CalculatorTests 
{
    [Fact] // This tells xUnit that this method is a test!
    public void Add_TwoPositiveNumbers_ReturnsCorrectSum() 
    {
        // 1. Arrange (Set up the environment)
        var calculator = new CalculatorService();
        int a = 5;
        int b = 10;
        
        // 2. Act (Perform the action)
        int result = calculator.Add(a, b);
        
        // 3. Assert (Verify the outcome using xUnit's Assert class)
        Assert.Equal(15, result);
    }
}

To run it, open your terminal in the tests folder and type dotnet test.

Moq (Mocking Dependencies)

What if you want to test a Controller or Endpoint that heavily depends on a database? You absolutely do not want your Unit Tests connecting to a real production database!

Instead, we use a mocking framework like Moq to create fake versions of our dependencies.

dotnet add package Moq
public class UserControllerTests
{
    [Fact]
    public void GetUser_WhenUserExists_ReturnsOk()
    {
        // Arrange: Create a fake IUserRepository
        var mockRepo = new Mock<IUserRepository>();
        
        // Program the fake repository to return 'Alice' when asked for Id '1'
        var fakeUser = new User { Id = 1, Username = "Alice" };
        mockRepo.Setup(repo => repo.GetUserById(1)).Returns(fakeUser);
        
        // Inject the fake repository into the real Controller!
        var controller = new UsersController(mockRepo.Object);
        
        // Act
        var result = controller.GetUser(1);
        
        // Assert: Ensure it returned a 200 OK!
        var okResult = Assert.IsType<OkObjectResult>(result); 
        var returnedUser = Assert.IsType<User>(okResult.Value);
        Assert.Equal("Alice", returnedUser.Username);
    }
}

Challenge for Day 29

Write a failing test for a method you haven’t built yet (Test Driven Development). Create an IsEven(int number) test that asserts 4 returns true. Then, build the method to make the test pass in dotnet test!

Tomorrow: The Final Day — Docker & Deployment!