Jedes Projekt, das länger als drei Monate lebt, hat ein Testdatenproblem. Am Anfang reichen ein paar JSON-Fixtures. Dann kommen Beziehungen zwischen Entitäten dazu, dann Migrationen, dann Edge Cases. Irgendwann verbringt das Team mehr Zeit mit der Pflege von Testdaten als mit dem Schreiben von Tests.
Das Problem mit statischen Fixtures
Fixtures — also hartcodierte Testdaten in JSON- oder YAML-Dateien — haben einen fundamentalen Nachteil: Sie sind gekoppelt an ein bestimmtes Schema. Jede Datenbankmigraton erfordert ein Update aller Fixtures. Bei zehn Tabellen mit je drei Fixture-Dateien sind das 30 Dateien, die synchron gehalten werden müssen.
Strategie 1: Factories
Factories erzeugen Testdaten programmatisch. Statt den Zustand zu beschreiben, beschreiben sie den Prozess.
class UserFactory {
private overrides: Partial<User> = {};
withRole(role: UserRole): this {
this.overrides.role = role;
return this;
}
withOrders(count: number): this {
this.overrides._orders = count;
return this;
}
build(): User {
return {
id: randomUUID(),
email: `test-${randomUUID().slice(0, 8)}@example.com`,
role: 'user',
createdAt: new Date(),
...this.overrides,
};
}
}
// Nutzung im Test
const admin = new UserFactory().withRole('admin').build();
const customerWithHistory = new UserFactory()
.withRole('customer')
.withOrders(5)
.build(); Der Vorteil: Wenn sich das Schema ändert, änderst du die Factory — nicht 30 Fixture-Dateien. Und der Testcode dokumentiert die Intention: “Ein Admin” oder “Ein Kunde mit Bestellhistorie”.
Strategie 2: Database Snapshots
Für Integrationstests, die eine realistische Datenmenge brauchen, sind Snapshots oft die pragmatischste Lösung.
# Snapshot erstellen (einmalig oder in CI)
pg_dump --format=custom \
--exclude-table-data='audit_log' \
--exclude-table-data='sessions' \
testdb > fixtures/baseline.dump
# Vor jedem Testlauf wiederherstellen
pg_restore --clean --if-exists \
--dbname=testdb fixtures/baseline.dump Strategie 3: Synthetische Daten
Für Tests, die realistische Datenverteilungen brauchen — etwa Performance-Tests oder ML-Pipelines — reichen weder Factories noch Snapshots. Hier kommen synthetische Daten ins Spiel.
Der Schlüssel ist, die statistischen Eigenschaften der Produktionsdaten zu reproduzieren, ohne die eigentlichen Daten zu kopieren. Das löst gleichzeitig das DSGVO-Problem: Synthetische Daten sind keine personenbezogenen Daten.
Die Pipeline: Alles zusammen
In der Praxis kombiniert man die Strategien:
| Testtyp | Strategie | Begründung |
|---|---|---|
| Unit Tests | Factories | Schnell, isoliert, schema-unabhängig |
| Integration Tests | Snapshots + Factories | Realistische Basis, gezielte Erweiterung |
| Performance Tests | Synthetische Daten | Große Mengen, realistische Verteilung |
| E2E Tests | Snapshots | Konsistenter, bekannter Zustand |
Testdaten in CI: Praktische Hinweise
- Testdaten gehören in die Versionskontrolle. Snapshots als Artefakte, Factory-Definitionen als Code.
- Jeder Testlauf startet mit einem definierten Zustand. Keine Abhängigkeiten zwischen Tests.
- Datenbankmigrationen testen. Ein Snapshot von vor drei Monaten + alle Migrationen = aktuelle Schema-Validierung.
- Datenvolumen variieren. Was mit 10 Datensätzen funktioniert, kann mit 10.000 scheitern.
Fazit
Testdatenmanagement ist kein glamouröses Thema. Aber es ist eines, das über die Zuverlässigkeit einer CI-Pipeline entscheidet. Die Investition in eine durchdachte Strategie zahlt sich in jeder Pipeline-Minute zurück, die nicht mit flaky Tests verschwendet wird.