Wzorce testowania integracyjnego
Twoje testy jednostkowe mockują bazę danych i przechodzą z wyróżnieniem. Potem wdrażasz i odkrywasz, że migracja Drizzle ORM zmieniła typ kolumny z varchar(255) na text, co spowodowało, że Twój unique constraint po cichu przestał działać. Mock nigdy o tym nie wiedział. Testy integracyjne istnieją, żeby łapać rzeczy, które się zdarzają, gdy prawdziwe komponenty ze sobą rozmawiają — dokładnie tam, gdzie bugi się ukrywają.
Czego się nauczysz
Dział zatytułowany „Czego się nauczysz”- Wzorce testów integracyjnych z bazą danych z właściwym setup, teardown i izolacją
- Workflow testowania kontraktów API łapiących breaking changes między serwisami
- Strategie testowania kolejek wiadomości, cache’ów i integracji z zewnętrznymi serwisami
- Prompty AI generujące kompletną infrastrukturę testową, nie tylko przypadki testowe
- Techniki utrzymywania testów integracyjnych na tyle szybkich, by działały w CI
Testowanie integracyjne z bazą danych
Dział zatytułowany „Testowanie integracyjne z bazą danych”Strategia testowej bazy danych
Dział zatytułowany „Strategia testowej bazy danych”Testy integracyjne potrzebują prawdziwej bazy danych, ale nie Twojej produkcyjnej. Skonfiguruj izolowaną testową bazę danych, która resetuje się między testami.
@src/lib/db/schema.ts @tests/setup.tsCreate a database integration test setup for our Drizzle ORM + PostgreSQL project:
1. A setup file that: - Creates a test database (or uses a container via testcontainers) - Runs all migrations before the test suite - Provides a clean transaction wrapper per test (rollback after each) - Exports a `getTestDb()` helper that returns an isolated database connection
2. An example test for UserRepository.create that: - Uses the real database (not mocks) - Verifies the record exists after creation - Checks unique constraint enforcement (duplicate email) - Verifies cascade deletes work correctly - Tests NULL handling for optional fields
Follow our existing test patterns in @tests/setup.tsclaude "Create a complete database integration test infrastructure:
1. Install and configure testcontainers for PostgreSQL: - Auto-start a PostgreSQL container before tests - Run migrations automatically - Provide per-test transaction isolation
2. Create /tests/integration/db-setup.ts with: - beforeAll: start container, run migrations - beforeEach: begin transaction - afterEach: rollback transaction - afterAll: stop container
3. Create /tests/integration/user-repository.integration.test.ts with tests: - Insert and retrieve a user (verify all fields round-trip correctly) - Unique email constraint (expect proper error, not crash) - Pagination (insert 50 users, verify page 2 returns correct 10) - Full-text search (if supported by our schema) - Concurrent inserts (same email, only one should succeed)
Run the tests after creating them. Fix any issues."Set up database integration testing for this project:1. Configure testcontainers for PostgreSQL2. Create test infrastructure with per-test isolation3. Generate integration tests for all repository classes4. Verify tests pass with a real database5. Add to CI pipeline configuration
Focus on testing database behaviors that unit tests with mocks cannot catch:- Constraint enforcement (unique, foreign key, check)- Transaction isolation and rollback- Query performance with realistic data volumes- Migration compatibilityTestowanie kontraktów API
Dział zatytułowany „Testowanie kontraktów API”Consumer-driven contract tests
Dział zatytułowany „Consumer-driven contract tests”Gdy Twój serwis konsumuje API innego serwisu, testy kontraktowe weryfikują integrację bez uruchamiania obu serwisów.
Testowanie kolejek wiadomości
Dział zatytułowany „Testowanie kolejek wiadomości”Testowanie integracji opartych na zdarzeniach wymaga weryfikacji publikacji, konsumpcji i kolejności wiadomości.
Testowanie serwisów zewnętrznych
Dział zatytułowany „Testowanie serwisów zewnętrznych”Wzorzec stub serwera
Dział zatytułowany „Wzorzec stub serwera”Dla zewnętrznych API, nad którymi nie masz kontroli (Stripe, Twilio, SendGrid), użyj stub serwerów symulujących zachowanie API.
Create a stub server for the Stripe API that we can use in integration tests.
The stub should handle:1. POST /v1/charges - Return success with a charge ID2. POST /v1/charges - Return 402 when amount > 999999 (simulates declined)3. POST /v1/refunds - Return success with refund ID4. GET /v1/charges/{id} - Return charge details for known IDs, 404 for unknown
Implementation:- Use Express with a random port (for parallel test execution)- Store created charges in memory (so GET returns what POST created)- Include a reset() method to clear state between tests- Export a factory: createStripeStub() returns { url, server, reset }
Save to /tests/stubs/stripe.stub.tsclaude "Create a comprehensive Stripe API stub for integration testing.
Save to /tests/stubs/stripe.stub.ts with:- Express server on random port- In-memory charge and refund storage- Configurable error responses for testing failure paths- Request logging for test assertions- Automatic cleanup in afterAll hooks
Then create /tests/integration/payment.integration.test.ts that:- Uses the Stripe stub instead of the real API- Tests: successful payment, declined card, refund, partial refund- Verifies our service handles each Stripe response correctly- Verifies retry logic for transient failures (500 responses)
Run tests after creation."Create stub servers for all external API integrations in this project.Check /src/clients/ for the external services we call.For each external service:1. Create a stub server in /tests/stubs/2. Support success and failure responses3. Create integration tests that use the stubs4. Verify our error handling for each failure mode
Include a test helper that starts all stubs and injects their URLsinto our service configuration.Utrzymywanie szybkich testów integracyjnych
Dział zatytułowany „Utrzymywanie szybkich testów integracyjnych”-
Równoległij z izolacją
Uruchamiaj niezależne zestawy testów równolegle. Każdy zestaw dostaje własny schemat bazy danych lub kontener.
-
Współdzielone kontenery testowe
Uruchom kontener bazy danych raz dla całego zestawu, nie na plik testowy. Użyj rollback transakcji do izolacji per-test.
-
Selektywne uruchamianie
Taguj testy i uruchamiaj tylko dotknięte testy integracyjne:
npm test -- --testPathPattern=integration/ordergdy pracujesz nad kodem zamówień. -
Seedowanie vs. setup per-test
Dla testów read-only seeduj dane referencyjne raz. Tworzenie danych per-test tylko dla testów, które je modyfikują.
-
Cache’owanie kontenerów w CI
Cache’uj obrazy Docker w pipeline CI, aby unikać ściągania ich przy każdym uruchomieniu.
Gdy coś się zepsuje
Dział zatytułowany „Gdy coś się zepsuje”“Testy integracyjne są niestabilne i losowo failują w CI.” Główne przyczyny: współdzielony stan bazy danych między testami, zahardkodowane porty kolidujące w równoległych uruchomieniach i asercje zależne od timingu. Użyj rollback transakcji do izolacji, losowych portów dla serwisów i pętli pollingu zamiast setTimeout dla operacji asynchronicznych.
“Testy przechodzą lokalnie, ale failują w CI.” Sprawdź różnice w wersjach bazy danych, brakujące zmienne środowiskowe i sieć Docker. Użyj testcontainers, aby zapewnić tę samą wersję bazy danych wszędzie. Przypnij obrazy kontenerów do konkretnych wersji.
“Testy integracyjne są zbyt wolne, by uruchamiać je na każdym commit.” Uruchamiaj tylko dotknięte testy integracyjne na commitach (na podstawie zmienionych plików). Uruchamiaj pełny zestaw przy merge do PR i w nocy. To daje szybki feedback podczas developmentu i kompleksową weryfikację przed merge.
“AI wygenerowało testy zależne od kolejności wstawiania.” Wyniki zapytań bazodanowych są nieuporządkowane, chyba że określisz ORDER BY. Poproś AI “sort results before asserting, or assert on set membership rather than array equality.”