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.