Integrationstests Überprüfen Sie, ob die Teile Ihrer API – Routen, Datenbank und Geschäftslogik – korrekt zusammenarbeiten, indem Sie echte Anfragen stellen und echte Antworten überprüfen. Im Gegensatz zu Unit-Tests (die alles verspotten) fangen Integrationstests Fehler an den Grenzen auf. Diese Anleitung zeigt, wie man sie gut schreibt.
📋 Table of Contents
Unit- und Integrationstests
| Aspekt | Unit-Test | Integrationstest |
|---|---|---|
| Geltungsbereich | Eine Funktion isoliert | Mehrere Teile zusammen (Strecke + DB) |
| Abhängigkeiten | Verspottet | Echte (oder Test-)Datenbank |
| Geschwindigkeit | Sehr schnell | Langsamer (echte E/A) |
| Fänge | Logikfehler | Integrations-/Verkabelungsfehler |
Sie wollen beides – Unit-Tests für die Logik, Integrationstests, um zu überprüfen, ob der gesamte Anfrage-Antwort-Fluss funktioniert.
Node.js: Testen mit Supertest
npm install --save-dev jest supertest
// Export your app WITHOUT calling listen() so tests can use it
// app.js
const express = require('express');
const app = express();
app.use(express.json());
app.get('/users/:id', getUser);
app.post('/users', createUser);
module.exports = app; // export the app
// server.js (separate) starts it
const app = require('./app');
app.listen(3000);
// users.test.js
const request = require('supertest');
const app = require('./app');
const db = require('./db');
describe('Users API', () => {
beforeEach(async () => {
await db.reset(); // clean database before each test
});
afterAll(async () => {
await db.close();
});
it('creates a user', async () => {
const res = await request(app)
.post('/users')
.send({ name: 'Alice', email: 'alice@example.com' });
expect(res.status).toBe(201);
expect(res.body).toMatchObject({ name: 'Alice' });
expect(res.body.id).toBeDefined();
});
it('returns 404 for missing user', async () => {
const res = await request(app).get('/users/999');
expect(res.status).toBe(404);
});
it('validates required fields', async () => {
const res = await request(app).post('/users').send({});
expect(res.status).toBe(400);
expect(res.body.error).toBeDefined();
});
});
Python: Testen mit pytest und TestClient
pip install pytest httpx
# test_users.py (FastAPI example)
import pytest
from fastapi.testclient import TestClient
from app.main import app
from app.database import get_test_db
client = TestClient(app)
@pytest.fixture(autouse=True)
def reset_db():
db = get_test_db()
db.reset()
yield
db.close()
def test_create_user():
response = client.post("/users", json={
"name": "Alice", "email": "alice@example.com"
})
assert response.status_code == 201
data = response.json()
assert data["name"] == "Alice"
assert "id" in data
def test_get_missing_user():
response = client.get("/users/999")
assert response.status_code == 404
def test_validation_error():
response = client.post("/users", json={})
assert response.status_code == 422
Verwendung einer Testdatenbank
Integrationstests benötigen eine echte (Test-)Datenbank – getrennt von Entwicklung und Produktion:
# Spin up a test database with Docker
docker run -d --name test-db -p 5433:5432 \
-e POSTGRES_DB=testdb -e POSTGRES_PASSWORD=test \
postgres:15-alpine
# Point tests at it via environment
DATABASE_URL=postgresql://postgres:test@localhost:5433/testdb
// Fast reset using transactions (rollback after each test)
beforeEach(async () => {
await db.query('BEGIN');
});
afterEach(async () => {
await db.query('ROLLBACK'); // undo all changes — fast and clean
});
Authentifizierte Endpunkte testen
describe('Protected routes', () => {
let token;
beforeAll(async () => {
const res = await request(app)
.post('/login')
.send({ email: 'test@example.com', password: 'password' });
token = res.body.accessToken;
});
it('allows access with valid token', async () => {
const res = await request(app)
.get('/protected')
.set('Authorization', `Bearer ${token}`);
expect(res.status).toBe(200);
});
it('rejects without token', async () => {
const res = await request(app).get('/protected');
expect(res.status).toBe(401);
});
});
Ausführen von Integrationstests in CI
# GitHub Actions with a service database
jobs:
test:
runs-on: ubuntu-latest
services:
postgres:
image: postgres:15
env:
POSTGRES_DB: testdb
POSTGRES_PASSWORD: test
ports: ['5432:5432']
options: --health-cmd pg_isready --health-interval 10s
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: '20' }
- run: npm ci
- run: npm run test:integration
env:
DATABASE_URL: postgresql://postgres:test@localhost:5432/testdb
Best Practices
- Einzeltests: Jeder Test sollte unabhängig sein – setzen Sie die Datenbank zwischen den Tests zurück
- Testen Sie den Vertrag: Statuscodes, Antwortform und Fehlerfälle überprüfen
- Fehlerpfade abdecken: Testen Sie 400er, 401er, 404er und Validierungsfehler, nicht nur den glücklichen Weg
- Verwenden Sie eine echte Testdatenbank: Verspotten Sie die Datenbank nicht in Integrationstests
- Halten Sie sie einigermaßen schnell: Das Transaktions-Rollback ist schneller als das Abschneiden von Tabellen
- In CI ausführen: Integrationstests fangen Verdrahtungsfehler auf, die Unit-Tests übersehen
Häufig gestellte Fragen
F: Sollte ich die Datenbank in Integrationstests verspotten?
A: Nein – es geht darum, echte Interaktionen, einschließlich der Datenbank, zu überprüfen. Verwenden Sie eine separate Testdatenbank. Wenn Sie es verspotten, wird es zu einem Unit-Test und übersieht die Integrationsfehler, die Sie abfangen möchten.
F: Wie halte ich Integrationstests schnell?
A: Verwenden Sie ein Transaktions-Rollback zwischen Tests, anstatt Tabellen abzuschneiden/neu zu erstellen. Führen Sie Tests parallel durch, sofern die Isolation dies zulässt. Verwenden Sie eine containerisierte Datenbank und halten Sie sie klein.
F: Wie viele Integrationstests benötige ich?
A: Decken Sie die kritischen Pfade jedes Endpunkts ab – Erfolg, Validierungsfehler, Authentifizierungsfehler und nicht gefundene Fälle. Unit-Tests behandeln Logikdetails; Integrationstests decken die Hauptverhaltensweisen jedes Endpunkts ab.
F: Integrationstests oder E2E-Tests?
A: Integrationstests überprüfen die API-Schicht (Routen + Datenbank). E2E-Tests überprüfen den vollständigen Benutzerfluss durch die Benutzeroberfläche. Beide haben ihren Wert – sie verfügen über mehr Integrationstests als E2E, da sie schneller und fokussierter sind.
F: Wie teste ich externe API-Aufrufe?
A: Verspotten Sie diese spezifischen Aufrufe (Sie möchten nicht, dass Tests Dienste Dritter betreffen), während Ihre Datenbank authentisch bleibt. Verspotten Sie nur echte externe Abhängigkeiten, nicht Ihre eigene Datenbank.
Fazit
Integrationstests überprüfen, ob Ihre API durchgängig funktioniert – Routen, Datenbank und Logik zusammen – und erkennen Verkabelungsfehler, die bei Unit-Tests übersehen werden. Verwenden SieSupertest mit Jest (Node.js) oder Pytest mit TestClient (Python), verweisen sie auf eine echte Testdatenbank (Zurücksetzen zwischen Tests über Transaktions-Rollback aus Geschwindigkeitsgründen) und decken Erfolgsfälle, Validierungsfehler, Authentifizierung und nicht gefundene Pfade ab. Führen Sie sie in CI mit einer Dienstdatenbank aus. In Kombination mit Unit-Tests für die Logik geben Ihnen Integrationstests die Gewissheit, dass Ihre API tatsächlich so funktioniert, wie Kunden es erwarten.
🔗 Share this article
✍️ Leave a Comment