In the previous post, we have paid attention on our project structure and organizing the code in multiple layers to achieve better readability and easier maintanence in the future. Now we can focus on long-term sustainability through versioning and testing.

In this final part of the series, we will:

  • Introduce API versioning so your app can evolve without breaking existing clients
  • Add unit tests to validate service and controller behavior

API Versioning?

Versioning helps your API evolve over time without breaking existing clients. Let’s enable it.

Step 1: Install Required Package

In the Sample.Api project, install:

dotnet add package Microsoft.AspNetCore.Mvc.Versioning

Step 2: Configure Versioning in Program.cs

In Program.cs, add the following:

    builder.Services.AddApiVersioning(options =>
    {
        options.AssumeDefaultVersionWhenUnspecified = true;
        options.DefaultApiVersion = new ApiVersion(1, 0);
        options.ReportApiVersions = true;
    });

This:

  • Defaults to version 1.0 when none is specified

  • Adds version info in response headers

  • Prepares your API to support multiple versions side-by-side

You can version your API using:

  • URL segments (/v1/controller)

  • Query string (?api-version=1.0)

  • Headers (e.g., api-version: 1.0)

We’ll use URL-based versioning in this example.

Step 3: Create a Versioned Controller

In Sample.Api/Controllers, decorate your controller like this:


[Route("api/v{version:apiVersion}/[controller]")]
[ApiVersion("1.0")]
[ApiController]
public class UserController : ControllerBase
{
....

That’s it—you’ve now added support for versioning via the URL.

Setting Up a Test Project

Now let’s set up unit testing using NUnit.

Step 1: Create a New Test Project

In the solution folder:

dotnet new nunit -n Sample.Api.Tests

Then add project references:

dotnet add Sample.Api.Tests reference Sample.Services
dotnet add Sample.Api.Tests reference Sample.Repositories
dotnet add Sample.Api.Tests reference Sample.DTO
dotnet add Sample.Api.Tests reference Sample.Entities

And install these packages:

dotnet add Sample.Api.Tests package Moq
dotnet add Sample.Api.Tests package Microsoft.AspNetCore.Mvc

Next, we can organize our tests in different folders, like /Services, /Repositories, /Controllers

Step 2: Writing Unit Tests for Services

Now, for the showing purpose, we can test UserService by mocking UserRepository:

[TestFixture]
public class UserTests
{
    [Test]
    public void GetUserByUsername_ReturnsDataFromRepo()
    {
        var mockRepo = new Mock<IUserRepository>();
        mockRepo.Setup(r => r.GetUser("Test")).Returns(new User()
        {
            Id = 1,
            Email = "test mail",
            Username = "Test",
        });

        var service = new UserService(mockRepo.Object);
        var user = service.GetUser("Test");

        Assert.That(user.Email, Is.EqualTo("test mail"));
    }
}

Here, we have shown one mocking example and how to isolate our service testing from depencency on repository. Similar can be done with controllers as well. For more details on testing and mocking, you can take a look at some of the previous posts for unit testing and mocking.

Summary

With versioning and testing in place, your API is now ready for real-world use:

  • Versioning allows the API to grow without breaking existing clients

  • Unit tests help you catch regressions early and keep logic robust

This wraps up our step-by-step guide to building a clean, testable, and production-ready .NET 9 Web API using best practices. Entire code can be found at my github repository.

Thanks for following the series!

Happy coding!