Testy integracyjne w ASP.NET Core — WebApplicationFactory krok po kroku
Test jednostkowy sprawdza jedną klasę w izolacji. Test integracyjny sprawdza, czy cały pipeline — routing, middleware, dependency injection, serializacja JSON — faktycznie działa razem, wysyłając prawdziwe żądanie HTTP do aplikacji uruchomionej w pamięci.
WebApplicationFactory — uruchomienie aplikacji w pamięci
using Microsoft.AspNetCore.Mvc.Testing;
using Xunit;
public class ZamowieniaApiTests : IClassFixture<WebApplicationFactory<Program>>
{
private readonly HttpClient _client;
public ZamowieniaApiTests(WebApplicationFactory<Program> fabryka)
{
_client = fabryka.CreateClient();
}
[Fact]
public async Task GetZamowienie_IstniejaceId_Zwraca200()
{
var odpowiedz = await _client.GetAsync("/api/zamowienia/1");
odpowiedz.EnsureSuccessStatusCode();
}
}WebApplicationFactory<Program> uruchamia całą aplikację (łącznie z rzeczywistym Program.cs, middleware i konfiguracją DI) w pamięci procesu testowego — _client to prawdziwy HttpClient, który wysyła żądania do tej instancji, bez otwierania portu sieciowego.
Odczyt i deserializacja odpowiedzi
[Fact]
public async Task GetZamowienie_IstniejaceId_ZwracaPoprawneDane()
{
var odpowiedz = await _client.GetAsync("/api/zamowienia/1");
odpowiedz.EnsureSuccessStatusCode();
var zamowienie = await odpowiedz.Content.ReadFromJsonAsync<ZamowienieDto>();
Assert.NotNull(zamowienie);
Assert.Equal(1, zamowienie.Id);
}ReadFromJsonAsync<T> deserializuje odpowiedź tak samo, jak zrobiłby to prawdziwy klient — test przechodzi przez ten sam serializer JSON, który działa w produkcji.
Podmiana bazy danych na testową
Domyślnie WebApplicationFactory uruchamia aplikację z tą samą konfiguracją co produkcja — łącznie z prawdziwym connection stringiem do bazy danych. W testach chcesz to podmienić na bazę w pamięci:
public class TestowaFabryka : WebApplicationFactory<Program>
{
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.ConfigureServices(services =>
{
var opisDeskryptora = services.SingleOrDefault(
d => d.ServiceType == typeof(DbContextOptions<ZamowieniaDbContext>));
if (opisDeskryptora != null) services.Remove(opisDeskryptora);
services.AddDbContext<ZamowieniaDbContext>(options =>
options.UseInMemoryDatabase("TestowaBaza"));
});
}
}
public class ZamowieniaApiTests : IClassFixture<TestowaFabryka>
{
private readonly HttpClient _client;
public ZamowieniaApiTests(TestowaFabryka fabryka) => _client = fabryka.CreateClient();
}ConfigureWebHost pozwala nadpisać rejestracje z kontenera DI już po tym, jak Program.cs je skonfigurował — usuwasz prawdziwy DbContext i rejestrujesz w jego miejsce wersję z bazą w pamięci, zanim CreateClient() uruchomi aplikację.
Test POST — zapis danych i weryfikacja
[Fact]
public async Task PostZamowienie_PoprawneDane_Zwraca201IZapisujeWBazie()
{
var noweZamowienie = new { KlientId = 5, Suma = 199.99m };
var odpowiedz = await _client.PostAsJsonAsync("/api/zamowienia", noweZamowienie);
Assert.Equal(HttpStatusCode.Created, odpowiedz.StatusCode);
var lokalizacja = odpowiedz.Headers.Location;
var sprawdzenie = await _client.GetAsync(lokalizacja);
sprawdzenie.EnsureSuccessStatusCode();
}Ten test sprawdza cały cykl: żądanie POST trafia przez routing do kontrolera, kontroler zapisuje przez DbContext do bazy w pamięci, zwraca 201 Created z nagłówkiem Location — a kolejne żądanie GET pod tym adresem faktycznie znajduje zapisany rekord. To coś, czego pojedynczy test jednostkowy kontrolera nigdy nie sprawdzi, bo mockowałby DbContext zamiast używać prawdziwego zapisu i odczytu.
Testy integracyjne vs jednostkowe — kiedy który
Test jednostkowy z Moq sprawdza logikę jednej klasy w izolacji — szybki, precyzyjny, ale nie wykryje błędu w konfiguracji routingu, złej rejestracji w DI, czy niekompatybilnego mapowania JSON. Test integracyjny z WebApplicationFactory sprawdza, czy te elementy faktycznie współpracują — wolniejszy (uruchamia całą aplikację), ale wyłapuje błędy integracji, których żaden test jednostkowy nie zobaczy. W praktyce potrzebujesz obu: dużo testów jednostkowych dla logiki biznesowej, kilku(nastu) testów integracyjnych dla najważniejszych ścieżek API.
Podsumowanie
WebApplicationFactory<Program> uruchamia całą aplikację ASP.NET Core w pamięci procesu testowego, dając prawdziwy HttpClient do wysyłania żądań przez pełny pipeline (routing, middleware, DI, serializacja). ConfigureWebHost pozwala podmienić zależności — najczęściej bazę danych na wersję w pamięci — zanim aplikacja wystartuje. To testy wolniejsze i bardziej kosztowne w utrzymaniu niż jednostkowe, ale sprawdzające coś, czego mock nigdy nie wykryje: czy poszczególne elementy aplikacji faktycznie działają razem.
🚀 Co dalej?
Zobacz to w praktyce na wideo i pobierz darmową roadmapę, żeby ułożyć naukę w spójną ścieżkę do pierwszej pracy.
- 🗺️ Pobierz darmową roadmapę Junior .NET Developer — 12 kroków od podstaw C# do pierwszej pracy: dev-hobby.pl
- 🎬 Subskrybuj kanał YouTube — nowe filmy co tydzień.
Zamień wiedzę w umiejętności
Pobierz darmową Roadmapę .NET i ułóż takie tematy jak ten w spójną ścieżkę do pierwszej pracy.
Pobieram roadmapę →
1 comment