Integration Tests with WebApplicationFactory
On this page
Production incident
A new middleware is added before UseAuthentication. Suddenly all endpoints behave differently. Unit tests pass because they bypass the pipeline. In production, auth headers are ignored and users get 401/403 storms. Another time, JSON serialization settings change and clients break. These failures only show up when you run the real application pipeline. That is what WebApplicationFactory is for.
Symptoms
- Endpoints fail due to middleware order or DI wiring.
- Model binding/validation behaves differently than expected.
- Auth/authorization policies misbehave after refactors.
- Serialization changes break clients silently.
Root causes
- Testing only controllers/services directly, not the full pipeline.
- Integration tests coupled to real infra (shared DB) causing flakiness.
- No isolated configuration for tests; secrets and prod settings leak into test runs.
Diagnosis
# Find integration test harness usage grep -R "WebApplicationFactory" -n test grep -R "CreateClient" -n test grep -R "ConfigureWebHost" -n test
Anti-pattern
- Integration tests that hit real external services.
- Tests that depend on execution order or shared state across tests.
- Hardcoding ports and assuming environment variables.
Correct pattern
Boot the app in-memory, override configuration, and use a real HttpClient against the test server. Replace external dependencies with fakes or Testcontainers (next topic).
Minimal test server pattern
public class ApiFactory : WebApplicationFactory<Program>
{
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.UseEnvironment("Test");
builder.ConfigureAppConfiguration((ctx, cfg) =>
{
// override config for tests if needed
});
builder.ConfigureServices(services =>
{
// Replace external HTTP clients, message buses, etc.
});
}
}
public class HealthTests : IClassFixture<ApiFactory>
{
private readonly HttpClient _client;
public HealthTests(ApiFactory factory)
{
_client = factory.CreateClient();
}
[Fact]
public async Task Ready_returns_200()
{
var resp = await _client.GetAsync("/health/ready");
Assert.True(resp.IsSuccessStatusCode);
}
}
What to assert (production-focused)
- Routing + status codes + content types.
- Auth: correct 401 vs 403 behavior.
- Validation: bad input returns predictable 400 with problem details.
- Headers: security headers, caching headers, correlation id echoes.
- Serialization contracts: stable JSON shapes.
Security and performance impact
- Security: pipeline tests catch missing auth middleware, broken policies, missing headers.
- Performance: keep integration tests targeted. Too many heavy tests slow CI; use them where they catch real breakage.
Operational notes
- CI: run integration tests on PRs for critical services.
- Rollout: if a production incident was caused by middleware/config, write an integration test that reproduces it.
- Rollback: integration tests protect you from shipping the same regression again during rollback/hotfix cycles.
Checklist
- Integration tests boot the real pipeline with WebApplicationFactory.
- External dependencies are replaced or containerized (no real internet calls).
- Tests are isolated and deterministic.
- Critical contracts (auth/validation/serialization) are asserted.
- Incident regressions become tests.