Begriff
Integration Test
Warum wichtig?
Dieser Begriff ist ein Knoten im SengakujiWorks-Wissensnetz. Nutze Level 0 für die erste Einordnung, Level 1 für Praxis, Level 2 für technische Struktur und Level 3 für Grenzen, Fallstricke und Expertenkontext.
Ein Integration Test prüft, ob zwei Bausteine zusammenpassen. Dein Code (Baustein A) ruft die Datenbank (Baustein B) auf. Ein Unit Test würde die Datenbank "faken" (Mocking). Er testet nur den Code. Der Integration Test testet wirklich gegen die Datenbank. Er prüft: "Wenn ich den User speichere, landet er wirklich in der Tabelle?" Er findet Fehler, die Unit Tests nicht finden:
- Falsches SQL-Passwort.
- Tabelle existiert nicht.
- Falsche Spaltennamen.
Merksatz: Eine Teststufe, bei der einzelne Softwaremodule kombiniert und als Gruppe getestet werden, um Interaktionsfehler zwischen ihnen (z. B. Datenbank, API, Microservice) aufzudecken.
Mit Testcontainers (Java/Go/Node). Der Test startet beim Ausführen einen echten Docker-Container (z. B. Postgres).
// Test start
const db = await TestContainer.start('postgres:15');
await app.connect(db.url);
// Test Logik
await app.createUser({ name: "Hans" });
const user = await db.query("SELECT * FROM users");
expect(user.name).toBe("Hans");
// Test ende
await db.stop();
Das ist langsamer als Unit Tests, aber viel realistischer.
1. Broad vs. Narrow Integration Tests
- Narrow: Testet nur die Interaktion mit einem externen Service (z. B. nur DB). Mockt alles andere (Redis, Stripe). Schnell und stabil.
- Broad: Testet fast alles zusammen (Subsystem-Test). Geht fast schon Richtung E2E. Teurer. Experten bevorzugen "Narrow Integration Tests" für jeden Adapter.
2. Contract Testing (Pact)
Wenn du gegen eine externe API (Microservice von Team B) testest, kannst du nicht immer deren Service starten.
Du nutzt Contract Tests.
Du definierst einen Vertrag (Contract): "Wenn ich /users/1 GETte, kommt { id: 1 } zurück."
Team B muss in seiner CI beweisen, dass sie den Vertrag einhalten.
Du brauchst dann im Integration Test gar keinen echten Service von Team B mehr, sondern nutzt einen "Stub", der garantiert korrekt ist.
3. In-Memory DBs vs. Docker
Früher nutzte man H2 oder SQLite (In-Memory) für Tests, obwohl Prod auf Oracle lief. Das ist ein Anti-Pattern ("False Confidence"). SQLite verhält sich anders als Postgres (z. B. bei JSONB oder Locking). Dank Docker (Testcontainers) kann man heute immer die echte DB-Engine nehmen.
Testcontainers und das Tear-Down Dilemma
Modernes Integration Testing mit Toolings wie Testcontainers spinnt echte Docker-Datenbanken hoch (z. B. eine leere PostgreSQL pro CI-Run). Das Hochfahren (Booten) des Images kostet oft spürbare Sekunden. Startet die CI-Pipeline hunderte Tests, droht ein massiver Overhead. Architektonische Lösung: Anstatt für jeden Spec/Test einen neuen Container hochzufahren, initiiert man den Container durch Boot-Scopes einmalig pro Test-Space globale Ebene (singleton). Der Flaschenhals ist nun das "Tear-Down" oder "Truncating" der Tabellen zwischen den Testfällen, um asynchrone Kreuzkontamination der Daten zu vermeiden ("Idempotenz").
Database Transaction Rollbacks
Anstatt am Ende jedes Datenbank-Integration-Tests alle Tabellen mühselig per DELETE FROM oder TRUNCATE Skripten zu flushen (was teuer und oft fehlerhaft ist), nutzt die Elite Framework-gestützte Database Transaction Rollbacks.
Jeder einzelne Testlauf wird radikal in einen START TRANSACTION Block gepackt.
Das Insert in die DB passiert im Scope der ungecommitteten Transaction. Der Assertion-Check prüft, ob die Reihen da sind.
Am absoluten Ende führt das Framework (z.B. Spring Boot oder Django Test Runner) ein deterministisches ROLLBACK aus, statt eines COMMIT.
Die in-Memory geschriebenen Daten verschwinden augenblicklich im Void. Die Datenbank ist millisekundenschnell zurückgesetzt für den nächsten Test, ohne jemals Physische I/O-Löschoperationen zu orchestrieren.
Consumer-Driven Contract Testing (Pact Broker)
Contract Tests in Microservice-Biotopen lösen das "Ich Mocke den Kollegen kaputt"-Drama. Die Evolution des Mocking lautet Consumer-Driven Contracts. Das rufende Team (Consumer) pusht während seiner Integration Tests eine vertragliche Garantie-JSON-Datei (den Pact) in ein zentrales Register (Vault/Pact Broker). Dieser Vertrag definiert exakt: "Ich sende Payload A, du gibst mir JSON Struktur B". Das Provider-Team zieht sich in deren CI-Pipeline stündlich alle Live-Verträge und spielt diese blind gegen die eigene Backend-Schnittstelle ab. Bricht ein Team aus Versehen das Schema durch Typos, scheitert der Pipeline-Build, bevor die Inkompatibilität den Master-Staging-Cluster crashen kann.
Quick-Check
Sind Integration Tests "White Box"?
Meistens "Grey Box". Du kennst den Code und die SQL-Queries, aber du testest das Verhalten des ganzen Moduls von außen (API oder DB-Interface).Dürfen Integration Tests das Netzwerk nutzen?
Ja, aber kontrolliert (Localhost / Docker). Wenn sie ins "echte" Internet gehen (z. B. Stripe API), werden sie langsam und flaky. Besser: Mockserver (WireMock) für externe APIs.Wo in der Pyramide?
In der Mitte. Du solltest mehr Unit Tests haben (hunderte), aber weniger Integration Tests (dutzende), und ganz wenige E2E Tests (eine Handvoll).