Integration tests with WebApplicationFactory and Testcontainers, against a real PostgreSQL.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | // ====================================================================== // Q: How do you test an API that uses PostgreSQL? // // Integration tests with WebApplicationFactory and Testcontainers, against a // real PostgreSQL. Not the InMemory provider: it doesn't check constraints, // transactions or SQL. // An authorization test like the one above is exactly what AI won't write on its // own. Mentioning it earns you points. // ====================================================================== public class ApiFactory : WebApplicationFactory<Program>, IAsyncLifetime { private readonly PostgreSqlContainer _pg = new PostgreSqlBuilder() .WithImage("postgres:16-alpine").Build(); protected override void ConfigureWebHost(IWebHostBuilder b) => b.ConfigureTestServices(s => { s.RemoveAll<DbContextOptions<AppDbContext>>(); s.AddDbContext<AppDbContext>(o => o.UseNpgsql(_pg.GetConnectionString())); }); public async Task InitializeAsync() { await _pg.StartAsync(); using var scope = Services.CreateScope(); await scope.ServiceProvider.GetRequiredService<AppDbContext>().Database.MigrateAsync(); } public new Task DisposeAsync() => _pg.DisposeAsync().AsTask(); } public class OrdersTests(ApiFactory f) : IClassFixture<ApiFactory> { [Fact] public async Task Customer_cannot_read_other_customers_order() { var client = f.CreateClientAs(customerId: 1); // helper with a test auth handler var res = await client.GetAsync("/api/orders/999"); // belongs to customer 2 Assert.Equal(HttpStatusCode.NotFound, res.StatusCode); } } |