ASP.NET Core cookbook · Intermediate
Write integration tests with WebApplicationFactory
Unit tests pass but routing, auth, validation and serialization bugs reach production.
Approach
Use WebApplicationFactory<Program> to run the real app in memory, replace external dependencies with
ConfigureTestServices, and call endpoints with the factory's HttpClient.
Solution
// Program.cs (the app under test)
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddSingleton<IClock, SystemClock>();
var app = builder.Build();
app.MapGet("/api/greeting", (IClock clock) => new { message = clock.Now.Hour < 12 ? "Good morning" : "Good afternoon" });
app.Run();
public interface IClock { DateTimeOffset Now { get; } }
public class SystemClock : IClock { public DateTimeOffset Now => DateTimeOffset.UtcNow; }
public partial class Program; // makes Program visible to the test project
// tests/GreetingTests.cs
using System.Net.Http.Json;
using Microsoft.AspNetCore.Mvc.Testing;
using Microsoft.AspNetCore.TestHost;
using Microsoft.Extensions.DependencyInjection;
using Xunit;
public class GreetingTests(WebApplicationFactory<Program> factory) : IClassFixture<WebApplicationFactory<Program>>
{
[Fact]
public async Task Says_good_morning_before_noon()
{
var client = factory.WithWebHostBuilder(b => b.ConfigureTestServices(services =>
services.AddSingleton<IClock>(new FixedClock(new DateTimeOffset(2026, 10, 26, 9, 0, 0, TimeSpan.Zero)))))
.CreateClient();
var body = await client.GetFromJsonAsync<Greeting>("/api/greeting", TestContext.Current.CancellationToken);
Assert.Equal("Good morning", body!.Message);
}
record Greeting(string Message);
class FixedClock(DateTimeOffset now) : IClock { public DateTimeOffset Now => now; }
}
Pitfalls
- Forgetting
public partial class Program, so the test project can't reference the entry point. - Replacing the database with EF Core's in-memory provider; use a real engine (Testcontainers) for data tests.
- Sharing state between tests through a single factory without resetting data.
Variations
- Subclass
WebApplicationFactory<Program>to centralise test configuration and Testcontainers setup. - Add a test authentication handler to simulate users and roles.
Further reading: Integration tests
Reference: https://learn.microsoft.com/en-us/aspnet/core/test/integration-tests
More ASP.NET Core recipes
- Accept file uploads with size and type limits
- Add OpenTelemetry traces and metrics
- Apply EF Core migrations safely during deployment
- Bind and validate options at startup
- Cache responses with output caching and evict by tag
- Call external APIs with typed HttpClients and resilience
- Configure CORS for a browser front end
- Expose liveness and readiness health checks