Wzorce testowania integracyjnego
ENTestowanie integracyjne z agentami AI polega na generowaniu testów, które uruchamiają kod na prawdziwej bazie danych, brokerze wiadomości w kontenerze i stubach zewnętrznych API na poziomie sieci, a potem na dowodzie, że każdy test pada, gdy jego złącze się psuje. Kluczowy warunek: agent musi zobaczyć każdy test na czerwono choć raz, inaczej zestaw dowodzi tylko, że kod się uruchamia.
Ta strona jest dla programistów, którzy proszą agenta o „testy integracyjne”, i dla tech leadów, którzy decydują, czy takie testy mogą blokować merge. Typowa porażka: agent mockuje repozytorium, pisze 40 zielonych testów, a migracja, która zmienia email z varchar(255) na text i odtwarza tabelę bez unikalnego indeksu, i tak trafia na produkcję. Tydzień później w bazie pojawiają się zduplikowane konta, a żaden test nigdy nie dotknął Postgresa.
Co wyniesiesz z testowania integracyjnego
Dział zatytułowany „Co wyniesiesz z testowania integracyjnego”- Prompt, który buduje harness testowy: kontener Postgresa uruchamiany raz na przebieg, zastosowane migracje i rollback po każdym teście.
- Prompt do testów repozytorium, które może wywrócić tylko prawdziwa baza: ograniczenia, transakcje, współbieżność, JSON i strefy czasowe.
- Prompt do testów konsumentów kolejki, który odpytuje o wynik zamiast czekać na sztywno i obejmuje ponowne dostarczenie oraz dead-letter queue.
- Tabelę decyzyjną dla stubów zewnętrznych API i prompt stuba wzorowanego na Payment Intents API Stripe’a.
- Kontrolę wstrzykiwania błędów złącze po złączu, która mówi, czy zestaw łapie błędy, bez czytania go linijka po linijce.
Jakie błędy powinien łapać test integracyjny?
Dział zatytułowany „Jakie błędy powinien łapać test integracyjny?”Test integracyjny jest wart swojego kosztu tylko tam, gdzie mock nie może zawieść. Jeśli zachowanie to czysta logika, należy do testu jednostkowego. Jeśli to ścieżka użytkownika przez UI, należy do testu end-to-end. Testy integracyjne leżą pomiędzy: na złączach, gdzie twój kod spotyka coś, czego nie kontroluje.
| Złącze | Czego mock nie złapie | Prawdziwa zależność w teście |
|---|---|---|
| Baza danych | Ograniczenia unique, klucze obce i check; rollback transakcji; blokady i izolacja; typy kolumn po migracji | Postgres (albo twój silnik) w kontenerze, ta sama wersja główna co na produkcji |
| Broker wiadomości | Klucze routingu, moment potwierdzenia (ack), ponowne dostarczenie, routing do dead-letter, kolejność | RabbitMQ, Kafka albo twój broker w kontenerze |
| Cache | Serializacja, wygasanie TTL, kolizje kluczy między tenantami | Redis w kontenerze |
| Zewnętrzne API HTTP | Kształt żądań, kody błędów, ponowienia i timeouty w twoim kliencie | Stub na granicy sieci; tryb testowy dostawcy w zaplanowanym przebiegu |
Sąsiednie tematy mają własne strony. Zachowanie endpointów i kontrakty consumer-driven z Pact są w testowaniu API. Fabryki, dane startowe i zanonimizowane fixture’y są w zarządzaniu danymi testowymi. Ta strona obejmuje harness i złącza.
Postaw prawdziwą bazę testową, którą agent sam uruchomi
Dział zatytułowany „Postaw prawdziwą bazę testową, którą agent sam uruchomi”Zanim agent napisze pierwszy test, potrzebuje harnessu, który sam uruchomi i posprząta. Testcontainers to typowy wybór dla Node, Javy, Pythona i Go: proces testów startuje jednorazowy kontener, odczytuje zmapowany port i usuwa kontener na końcu. Wersje sprawdzone w npm 2026-09-26: testcontainers i @testcontainers/postgresql 12.1.0.
Strategia izolacji to decyzja, która kształtuje każdy kolejny test. Wybierz ją przed promptem i wpisz ją do promptu.
| Strategia izolacji | Szybka? | Psuje się, gdy |
|---|---|---|
Jedna transakcja na test, rollback w afterEach | Najszybsza | Testowany kod sam commituje albo otwiera własną transakcję, albo test potrzebuje dwóch połączeń (współbieżność, blokady) |
| Osobny schemat albo baza na workera, czyszczona między testami | Szybka | Testy w jednym workerze współdzielą stan, jeśli czyszczenie pominie tabelę |
| Nowy kontener na plik testów | Wolna | Rzadko błędna, ale czas startu dominuje nad całym zestawem |
Większość zestawów domyślnie używa rollbacku, a osobnego schematu na workera tylko w testach, które potrzebują prawdziwych commitów. Napisz to w prompcie, bo inaczej agent wybierze jedną strategię i zastosuje ją wszędzie.
Uruchom prompt harnessu w Claude Code, Codeksie albo Cursorze
Dział zatytułowany „Uruchom prompt harnessu w Claude Code, Codeksie albo Cursorze”Prompt jest ten sam w każdym narzędziu. Różni się to, jak agent dostaje zgodę na uruchamianie Dockera i komendy testów bez pytania na każdym kroku.
Uruchom go interaktywnie przez claude albo headless, jako powtarzalne zadanie. Najpierw zapisz prompt jako prompts/integration-harness.md. --allowedTools z góry zatwierdza tylko komendy potrzebne do zadania, w tym jedną instalację, o którą prosi prompt, i żadnego innego pakietu (składnia flag sprawdzona przez claude --help w wersji 2.1.287):
# terminal, in the repository rootclaude -p "$(cat prompts/integration-harness.md)" \ --permission-mode dontAsk \ --allowedTools "Bash(docker info)" "Bash(npx vitest run *)" "Bash(npm install -D testcontainers @testcontainers/postgresql)" Edit WriteJeśli agent ma uruchamiać komendę migracji poza globalnym setupem Vitest, dopisz ją do listy, na przykład "Bash(npm run db:migrate)"; z --permission-mode dontAsk headless -p odrzuca u każdego dostawcy każdą komendę, która wymagałaby pytania (-p startuje w trybie uprawnień Manual; od v2.1.285 u zewnętrznych dostawców lub z wyłączoną telemetrią startuje w trybie auto).
Żeby te same zgody obowiązywały w każdej sesji, wpisz reguły Bash(...) do permissions.allow w .claude/settings.json.
Uruchom go w interaktywnym CLI przez codex albo headless przez codex exec. Żeby agent mógł edytować repozytorium i uruchamiać w nim komendy, użyj profilu uprawnień -c default_permissions=":workspace" (beta). Starsza flaga --sandbox workspace-write jest nadal akceptowana w 0.157.1 (sprawdzone przez codex exec --help). Nie łącz tych dwóch mechanizmów; OpenAI pisze, że profile i starszy sandbox się nie składają.
# terminal, in the repository root# permission profile (beta)codex exec -c default_permissions=":workspace" "$(cat prompts/integration-harness.md)"# or the legacy sandbox flag, still accepted in 0.157.1:codex exec --sandbox workspace-write "$(cat prompts/integration-harness.md)"To, czy sandbox dosięgnie demona Dockera, zależy od platformy i ustawień, dlatego prompt zaczyna się od docker info. Jeśli ten krok się nie powiedzie, uruchom kontenery poza sesją agenta i przekaż mu URL połączenia, jak opisano w środowiskach efemerycznych.
Wklej prompt do panelu Agent i dołącz package.json oraz katalog migracji przez @. Zatwierdź docker info i komendę testów, gdy agent o nie zapyta. W ustawieniach trybów uruchamiania (run modes) w Cursorze trzymaj docker info, komendę testów i instalację pakietów za zatwierdzeniem, zamiast pozwalać im działać bez nadzoru.
Daj agentowi dostęp do żywego schematu przez MCP
Dział zatytułowany „Daj agentowi dostęp do żywego schematu przez MCP”Bez dostępu do bazy agent wnioskuje typy kolumn i ograniczenia z plików migracji i myli się po migracji, która zmieniła tabelę w miejscu. Serwer MCP dla bazy danych pozwala mu odpytać information_schema, wylistować indeksy i uruchomić problematyczne zapytanie bezpośrednio na kontenerze.
DBHub (npm @bytebase/dbhub, 1.3.1 na dzień 2026-09-26) łączy się z PostgreSQL, MySQL, MariaDB, SQL Server, Oracle i SQLite przez jeden serwer. Kieruj go wyłącznie na jednorazową bazę, nigdy na współdzieloną.
DSN dla MCP to nie jest URL z danego przebiegu Testcontainers. Harness opisany wyżej mapuje Postgresa na losowy port hosta, który zmienia się przy każdym przebiegu, i eksportuje URL tylko wewnątrz procesu testów. W powłoce, w której dodajesz serwer, ten URL jest więc pusty, a skopiowany ręcznie przestaje działać po następnym przebiegu. Zamiast tego uruchom długo żyjący kontener do inspekcji schematu na stałym porcie, zastosuj na nim migracje i wyeksportuj jego URL, zanim dodasz serwer:
# terminal: use the same Postgres major version as productiondocker run -d --name it-schema -p 55432:5432 -e POSTGRES_PASSWORD=test postgres:<production major>export TEST_DATABASE_URL=postgres://postgres:test@localhost:55432/postgres# apply every migration to it with your real migration command, pointed at $TEST_DATABASE_URLUruchamiaj migracje na it-schema ponownie przy każdej zmianie schematu, żeby agent widział to samo co testy. Powłoka rozwija $TEST_DATABASE_URL w momencie dodawania serwera, a wartość trafia do twojej lokalnej, niecommitowanej konfiguracji MCP.
claude mcp add dbhub -- npx -y @bytebase/dbhub@latest --transport stdio --dsn "$TEST_DATABASE_URL"Domyślny zakres to local, więc wpis nie trafia do .mcp.json ani do gita.
codex mcp add dbhub -- npx -y @bytebase/dbhub@latest --transport stdio --dsn "$TEST_DATABASE_URL"Wpis trafia do ~/.codex/config.toml, poza repozytorium.
Dodaj serwer w mcpServers w pliku .cursor/mcp.json z "command": "npx" i "args": ["-y", "@bytebase/dbhub@latest", "--transport", "stdio", "--dsn", "<URL kontenera testowego>"]. Jeśli plik zawiera URL, trzymaj go poza gitem.
Nigdy nie dawaj agentowi DSN-a produkcji ani stagingu do tej pracy. Dostęp tylko do odczytu na potrzeby diagnostyki produkcji to inne zadanie z innymi serwerami; opcje i ich przełączniki read-only są na stronie serwery MCP dla baz danych. Nie używaj @modelcontextprotocol/server-postgres: pakiet jest przestarzały i zarchiwizowany (npm oznacza wersję 0.6.2 jako „no longer supported”; sprawdzone 2026-09-26).
Pisz testy bazy, które może wywrócić tylko prawdziwa baza
Dział zatytułowany „Pisz testy bazy, które może wywrócić tylko prawdziwa baza”Proś o zachowania, nie o metody. „Przetestuj OrderRepository” daje jeden test na metodę, który sprawdza wartość zwracaną. Lista zachowań bazy daje testy, które kończą się błędem, gdy baza nie zgadza się z twoim kodem.
Migracje zasługują na własny test. Unikalny indeks z przykładu na początku zniknął, gdy migracja przebudowała tabelę. Test, który stosuje wszystkie migracje na pustej bazie, a potem sprawdza ograniczenia, na których polegasz (a nie cały schemat), łapie tę klasę błędów w kilka sekund. Poproś agenta, żeby wygenerował listę ograniczeń z aktualnego schematu, przejrzyj ją raz i trzymaj test obok migracji.
Testuj kolejki wiadomości i asynchronicznych konsumentów
Dział zatytułowany „Testuj kolejki wiadomości i asynchronicznych konsumentów”Testy kolejek psują się na dwa sposoby, których nie znają testy bazy. Zależą od czasu, więc agent sięga po setTimeout(2000) i zestaw staje się jednocześnie wolny i niestabilny. A najciekawsze zachowanie to to, co dzieje się przy drugim dostarczeniu, którego test „szczęśliwej ścieżki” nigdy nie wywoła.
Stubuj zewnętrzne API na granicy sieci
Dział zatytułowany „Stubuj zewnętrzne API na granicy sieci”Przy API, których nie kontrolujesz, test potrzebuje czegoś, co odpowiada po HTTP tak jak dostawca. Wybór zależy od tego, co stub ma udowodnić.
| Opcja | Dowodzi | Nie widzi | Do czego |
|---|---|---|---|
Przechwytywanie w procesie (np. MSW, msw 2.15.0 w npm na dzień 2026-09-26) | Obsługi każdej odpowiedzi przez twojego klienta, bez zarządzania portem | Niczego poniżej klienta HTTP: TLS, DNS, ustawień proxy | Większości obsługi błędów po stronie klienta |
| Serwer stub na losowym porcie | Tego samego plus prawdziwe gniazda, timeouty i zerwane połączenia | Zachowań dostawcy, których nie zamodelowałeś | Ponowień, timeoutów, usług wołanych z osobnego procesu |
| Mock publikowany przez dostawcę, np. stripe-mock | Że żądania trafiają pod prawdziwe URL-e z poprawnymi parametrami, bo jest generowany ze specyfikacji OpenAPI Stripe’a | Zachowania: README mówi, że „does not attempt to reproduce the behavior of the real Stripe API at all”, a odpowiedzi są zakodowane na sztywno | Kontroli kształtu żądań obok własnego stuba |
| Tryb testowy dostawcy | Prawdziwego zachowania | Szybkości i determinizmu; wymaga sekretu w CI | Zaplanowanego zadania, które łapie rozjazd stuba, a nie każdego pull requesta |
Stub jest tak prawdziwy, jak jego model dostawcy. Buduj go z endpointów, które naprawdę woła twój produkcyjny klient, i uruchamiaj ten sam kod klienta na trybie testowym dostawcy według harmonogramu. Wtedy rozjazd wychodzi jako czerwony nocny przebieg, a nie incydent na produkcji.
Skąd wiesz, że zestaw integracyjny łapie błędy, skoro go nie czytasz?
Dział zatytułowany „Skąd wiesz, że zestaw integracyjny łapie błędy, skoro go nie czytasz?”Zielony zestaw integracyjny pokazuje, że kod działa na prawdziwych zależnościach. Nie pokazuje, że asercje zauważą zepsute złącze. Cztery kontrole dają ci ten dowód i żadna nie wymaga, żeby człowiek czytał testy.
-
Widziany na czerwono, dla każdego złącza. Każdy prompt powyżej kończy się tym, że agent psuje pilnowane zachowanie i wkleja czerwony wynik. Trzymaj ten raport w pull requeście. Test bez zapisanego czerwonego przebiegu nie może blokować merge’a.
-
Wstrzykiwanie błędów w harness. Raz na zestaw, nie na test: usuń ograniczenie, usuń granicę transakcji, potwierdź wiadomość przed commitem, każ stubowi zwrócić 500. Dla każdego błędu co najmniej jeden test musi zmienić się na czerwony. Szerzej opisuje to testowanie przez wstrzykiwanie błędów.
-
Wynik testów mutacyjnych (mutation score) dla warstwy repozytorium i klienta. Uruchom testy mutacyjne na plikach, których dotknęła zmiana. Ze StrykerJS zainstaluj go raz przez
npm install -D @stryker-mutator/core @stryker-mutator/vitest-runner(oba w wersji 10.0.0 w npm na dzień 2026-09-26), potem uruchomnpx stryker run --testRunner vitest --mutate "src/db/order-repository.ts"i porównaj wynik z progiem. Bez runnera Vitest Stryker wraca donpm test, które zwykle uruchamia testy jednostkowe, więc wynik nic nie mówi o tych testach. Wskaż runnerowi testy integracyjne przezvitest.configFilewstryker.config.json, podając konfigurację Vitest, która obejmuje tylkotests/integration/**. Bez lokalnej instalacjinpx strykerpobierze stary, przestarzały pakietstryker. Jak ustawić poprzeczkę, opisuje strona siła wyroczni. -
Odsetek niestabilnych testów przy wyłączonych ponowieniach. Uruchom projekt integracyjny trzy razy w losowej kolejności w CI. W Vitest to
npx vitest run --project integration --sequence.shuffle --retry 0, powtórzone trzy razy (flagi sprawdzone w pomocy Vitest 5.0.2). Każdy test, który raz przejdzie, a raz nie, trafia do kwarantanny i jest naprawiany, a nie ponawiany aż do zielonego.
Potem zablokuj wyrocznię: agent implementujący nie może edytować testów, które go oceniają, a CI uruchamia zestaw spoza jego diffu. Konfiguracja dla każdego narzędzia jest na stronie ochrona wyroczni.
Zatwierdzanie dzieli się tak samo jak przy testach API. Człowiek zatwierdza listę zachowań dla każdego złącza i strategię izolacji, bo to decyzje produktowe i dotyczące danych. Resztę przejmuje CI: zestaw, przebieg wstrzykiwania błędów, próg mutacji i kontrolę niestabilności. Pull request niesie raport „widziany na czerwono” i te wyniki jako pakiet dowodów.
Utrzymaj testy integracyjne na tyle szybkie, by szły w każdym pull requeście
Dział zatytułowany „Utrzymaj testy integracyjne na tyle szybkie, by szły w każdym pull requeście”Zestaw, który trwa 20 minut, ląduje w nocnym przebiegu i przestaje cokolwiek blokować. Utrzymasz go w każdym pull requeście dzięki tym nawykom, w tej kolejności:
- Jeden kontener na przebieg, nie na plik. Uruchom bazę i broker w globalnym setupie i współdziel je.
- Domyślnie rollback, commitowany schemat tylko tam, gdzie trzeba. Czyszczenie wszystkich tabel między testami kosztuje więcej niż rollback.
- Dane referencyjne ładuj raz. Kraje, plany i flagi funkcji idą do kroku seed; każdy test tworzy tylko wiersze, które zmienia.
- Przy każdym commicie uruchamiaj tylko dotknięte pliki. W Vitest podaj filtr ścieżki:
npx vitest run tests/integration/orders. Cały projekt uruchamiaj przed merge’em. - Przypinaj i cache’uj obrazy. Przypnij tagi obrazów i cache’uj pobrane obrazy w CI, żeby każdy przebieg pomijał pobieranie.
Kiedy testy integracyjne zawodzą
Dział zatytułowany „Kiedy testy integracyjne zawodzą”Zestaw kończy się w sesji agenta błędem połączenia z Dockerem. Sandbox agenta nie dosięga demona Dockera albo Docker nie działa. Co zrobić: uruchom docker info samodzielnie; jeśli działa poza sesją, uruchamiaj kontenery poza agentem i przekazuj URL połączenia przez zmienne środowiskowe. Wzorzec dla równoległych agentów opisują środowiska efemeryczne.
Testy przechodzą osobno, a razem nie, albo przechodzą lokalnie i padają w CI. Współdzielony stan bazy, port wpisany na sztywno albo inna wersja Postgresa. Co zrobić: izolacja przez rollback, port 0 dla każdego stuba i przypięta produkcyjna wersja główna w obu miejscach. Poproś agenta, żeby przed zgłoszeniem końca pracy uruchomił zestaw trzy razy w losowej kolejności (--sequence.shuffle w Vitest).
Agent „naprawił” czerwony test, osłabiając go. Szukaj expect.anything(), poszerzonego dopasowania błędu, usuniętej asercji albo testu przeniesionego do skip. Co zrobić: zablokuj edycję testów w sesjach implementujących i oznaczaj diffy, które zmniejszają liczbę asercji, jak opisuje ochrona wyroczni.
Agent zamockował bazę w teście integracyjnym. Trafił na trudny problem z setupem i podmienił repozytorium na atrapę, żeby było zielono. Co zrobić: dodaj w CI krok przed testami integracyjnymi, który pada, gdy dowolny plik w tests/integration/ mockuje twój własny kod, np. ! grep -rnE "vi\.mock\(['\"](\.|@/|src/)" tests/integration/ (dopasuj prefiksy do swoich aliasów importu), i powtórz w prompcie, że baza jest prawdziwa.
Test współbieżności zawsze przechodzi, nawet po usunięciu ograniczenia. Oba inserty poszły w tej samej transakcji albo na tym samym połączeniu, więc wyścigu nie było. Co zrobić: użyj dwóch połączeń z commitowanymi transakcjami i trzymaj raport „widziany na czerwono” jako dowód.
Asercje zależą od kolejności wierszy. Bez ORDER BY Postgres nie gwarantuje kolejności, a test przechodzi do pierwszego vacuum albo zmiany planu zapytania. Co zrobić: sortuj w zapytaniu albo porównuj jako zbiory.
Stub rozjechał się z dostawcą. Dostawca zmienił kod błędu, stub dalej zwraca stary, a produkcja źle obsługuje nowy. Co zrobić: zaplanowany przebieg w trybie testowym pierwszy zmieni się na czerwony; wygeneruj stub na nowo z aktualnych wywołań klienta i dokumentacji API dostawcy i dodaj nowy kod do stuba.