Course contentsShow
← C#
Lesson 1654 of 1,90815. Software Quality and TestingFree lesson

Testing API Controllers End-to-End

Execute HTTP requests against in-memory controllers to verify routing and model binding.

In this lesson, you will learn how to verify your API controllers' routing, model binding, and HTTP lifecycle by executing requests against an in-memory test server.

Testing API Controllers End-to-End

When we unit test controllers, we often mock dependencies (like IService) and invoke actions as if they were simple class methods. However, this misses critical infrastructure concerns: Does the route map correctly? Does the framework successfully bind JSON from the request body to our DTO? Does our ModelState validation trigger?

To catch these issues, we move beyond isolated unit tests and perform "end-to-end" (E2E) testing at the controller level. We do this by using WebApplicationFactory<TEntryPoint>, which bootstraps a minimal version of your application in memory.

Executing In-Memory Requests

By hosting the controller in memory, we can send actual HttpRequestMessage objects through the TestServer. The framework handles the full pipeline: routing, model binding, filters, and action execution.

public class UserApiTests : IClassFixture<WebApplicationFactory<Startup>>
{
    private readonly HttpClient _client;

    public UserApiTests(WebApplicationFactory<Startup> factory)
    {
        _client = factory.CreateClient();
    }

    [Fact]
    public async Task Post_ReturnsCreated_WhenModelIsValid()
    {
        // Arrange
        var content = new StringContent("{ \"username\": \"testuser\" }", Encoding.UTF8, "application/json");

        // Act
        // This hits the real route, runs model binding, and executes the controller action
        var response = await _client.PostAsync("/api/users", content);

        // Assert
        Assert.Equal(HttpStatusCode.Created, response.StatusCode);
    }
}

Why This Matters

In this approach, you aren't mocking the ModelState. If your model is missing a required property or the route attribute is misspelled, the test will fail, just as it would in production. This verifies the "glue" that holds your API together. Because we are testing through the HttpClient, we ensure that our API contract is intact and that the framework's serialization rules are applied correctly.

Key Takeaway: Testing API controllers end-to-end allows you to validate routing, model binding, and framework configuration by running your controller logic through the actual ASP.NET Core request pipeline in an isolated, in-memory environment.