Von der Wiki-Seite zur gelebten Praxis im Code Review
Eine Teststrategie, die nur im Wiki dokumentiert ist, verändert das Verhalten eines Teams nicht von selbst. Erst wenn Pyramidendenken im Code Review, in der Definition of Done und in einer schrittweisen Migration bestehender Tests verankert wird, entsteht eine gesunde Verteilung zwischen schnellen Unit Tests und teuren End to End Tests, die auch unter Zeitdruck Bestand hat.
Inhaltsverzeichnis
- 1. Warum eine dokumentierte Teststrategie allein nichts verändert
- 2. Die Testpyramide: Grundlagen und häufige Missverständnisse
- 3. Pyramiden-Denken im Code Review verankern
- 4. Definition of Done als Hebel für die Testverteilung
- 5. Die E2E-Falle: einfacher zu denken, aber teurer im Unterhalt
- 6. Testverteilung messen: Metriken statt Bauchgefühl
- 7. Inkrementelle Migration von ungesund zu gesund
- 8. Verantwortung und Ownership im Team verankern
- 9. Testpyramide-Patterns im direkten Vergleich
- 10. Zusammenfassung
- 11. FAQ
1. Warum eine dokumentierte Teststrategie allein nichts verändert
In fast jedem Team, das sich schon einmal mit der Testpyramide auseinandergesetzt hat, existiert irgendwo ein Confluence-Artikel oder eine README-Datei, die das Prinzip erklärt: viele Unit Tests, weniger Integrationstests, wenige End to End Tests. Trotzdem sieht die tatsächliche Suite in der Praxis oft anders aus, mit hunderten langsamen Cypress- oder Playwright-Tests und kaum Unit Tests für die eigentliche Geschäftslogik. Der Grund dafür ist selten Unwissenheit. Entwickler kennen das Konzept meistens, aber Wissen allein verändert kein Verhalten, wenn es keine Konsequenz hat, ob eine Pull Request die Pyramide respektiert oder ignoriert.
Eine Teststrategie, die nur dokumentiert, aber nirgends erzwungen oder auch nur sichtbar gemacht wird, verliert im Tagesgeschäft gegen den Druck eines nahenden Sprint-Endes. Wer unter Zeitdruck schnell eine Funktion absichern muss, greift zu dem Werkzeug, das sofort verständlich Vertrauen erzeugt, meist ein Browser-Test, der den kompletten Nutzerfluss simuliert. Das fühlt sich sicherer an als ein isolierter Unit Test mit Mocks, obwohl es langfristig teurer und fragiler ist. Ohne einen Mechanismus, der diese Entscheidung im Moment des Schreibens beeinflusst, bleibt die Pyramide eine Grafik im Wiki statt einer gelebten Praxis im Code.
2. Die Testpyramide: Grundlagen und häufige Missverständnisse
Das Grundprinzip der Testpyramide von Mike Cohn ist simpel: Unit Tests bilden die breite Basis, weil sie schnell laufen, isoliert sind und bei einem Fehlschlag exakt auf die fehlerhafte Codezeile zeigen. Integrationstests prüfen das Zusammenspiel mehrerer Komponenten, etwa Repository und Datenbank, und bilden die mittlere Schicht. End to End Tests simulieren echte Nutzerinteraktionen über die gesamte Anwendung hinweg und bilden die schmale Spitze, weil sie langsam, teuer im Unterhalt und anfällig für Flakiness sind. Die Pyramidenform beschreibt also nicht Wichtigkeit, sondern die empfohlene Verteilung nach Ausführungsgeschwindigkeit und Wartungskosten.
Ein häufiges Missverständnis ist, die Pyramide als reine Testzahl-Vorgabe misszuverstehen, etwa siebzig Prozent Unit Tests, zwanzig Prozent Integration, zehn Prozent End to End. Solche Zahlen sind grobe Orientierung, keine Zielvorgabe, die ohne Kontext blind erfüllt werden sollte. Entscheidender ist das dahinterliegende Prinzip: Jeder Test sollte auf der niedrigstmöglichen Ebene geschrieben werden, die den betreffenden Fehler zuverlässig erkennt. Das Gegenteil der Pyramide, die sogenannte Ice Cream Cone Anti-Pattern mit vielen manuellen und E2E-Tests und kaum Unit Tests, entsteht meist nicht bewusst, sondern schleichend über viele einzelne, isoliert nachvollziehbare Entscheidungen.
<!-- phpunit.xml: separate Testsuiten pro Pyramidenebene erzwingen sichtbare Trennung -->
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="vendor/phpunit/phpunit/phpunit.xsd"
bootstrap="vendor/autoload.php">
<testsuites>
<testsuite name="unit">
<directory>tests/Unit</directory>
</testsuite>
<testsuite name="integration">
<directory>tests/Integration</directory>
</testsuite>
<testsuite name="e2e">
<directory>tests/E2E</directory>
</testsuite>
</testsuites>
<!-- CI ruft unit und integration bei jedem Commit auf, e2e nur vor dem Merge -->
</phpunit>
3. Pyramiden-Denken im Code Review verankern
Der wirkungsvollste Hebel, um die Testpyramide vom Papier in den Alltag zu bringen, ist das Code Review. Statt allgemein zu fragen, ob ein Test existiert, sollte ein Reviewer gezielt fragen, auf welcher Ebene ein Test geschrieben wurde und ob diese Ebene angemessen ist. Fügt eine Pull Request einen neuen Playwright-Test für eine reine Preisberechnung hinzu, die keinerlei Browser-Interaktion erfordert, ist das ein legitimer Grund für einen Review-Kommentar, der eine Unit-Test-Alternative vorschlägt. Dieser Moment im Review ist der eigentliche Ort, an dem sich Kultur bildet, nicht die Dokumentation im Wiki.
Damit Reviewer diese Frage konsistent stellen, hilft eine kurze, konkrete Checkliste als Teil der Pull-Request-Vorlage, etwa die explizite Frage, ob der schnellste mögliche Test gewählt wurde, der den Fehlerfall zuverlässig abdeckt. Wichtig ist, dass diese Regel nicht dogmatisch gegen jeden E2E-Test angewendet wird. Ein neuer Checkout-Flow braucht durchaus einen End to End Test, aber die Randfallprüfung einer Rabattberechnung innerhalb dieses Flows gehört in einen Unit Test daneben, nicht in denselben Browser-Test. Reviewer, die konsequent nach der niedrigsten sinnvollen Ebene fragen, verschieben die Verteilung Pull Request für Pull Request in die richtige Richtung.
4. Definition of Done als Hebel für die Testverteilung
Eine Definition of Done, die nur "Tests vorhanden" verlangt, ist zu unspezifisch, um die Pyramide zu schützen. Effektiver ist eine Definition of Done, die explizit zwischen den Ebenen unterscheidet: Geschäftslogik braucht Unit-Test-Abdeckung, das Zusammenspiel mit externen Systemen braucht einen Integrationstest, und nur kritische Nutzerflüsse rechtfertigen einen zusätzlichen End to End Test. Ein Ticket gilt erst dann als fertig, wenn die Tests auf der richtigen Ebene existieren, nicht wenn irgendein Test grün ist. Diese Präzisierung macht aus einer vagen Absichtserklärung eine überprüfbare Regel, die sowohl der Entwickler selbst als auch der Reviewer gegen das Ticket abgleichen kann.
In der CI-Pipeline lässt sich diese Regel zusätzlich technisch unterstützen, indem ein Budget für die Laufzeit und Anzahl der E2E-Tests pro Pull Request definiert wird. Überschreitet eine Änderung dieses Budget deutlich, ist das ein Signal, dass vermutlich zu viel Logik über den Browser statt auf einer niedrigeren Ebene getestet wird. Das folgende Beispiel zeigt, wie ein solches Budget als Teil der Pipeline-Konfiguration sichtbar gemacht wird, statt nur informell im Teamgespräch zu existieren.
# .github/workflows/test-pyramid-gate.yml
name: Test Pyramid Gate
on: [pull_request]
jobs:
pyramid-budget:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run unit and integration tests (must run on every commit)
run: npm run test:unit && npm run test:integration
- name: Count new E2E test files in this PR
id: e2e_count
run: |
count=$(git diff --name-only origin/main...HEAD -- 'tests/e2e/**/*.spec.ts' | wc -l)
echo "count=$count" >> "$GITHUB_OUTPUT"
- name: Fail if E2E budget is exceeded without justification
if: steps.e2e_count.outputs.count > 3
run: |
echo "This PR adds more than 3 new E2E tests."
echo "Please confirm in the PR description why lower-level tests are insufficient."
exit 1
5. Die E2E-Falle: einfacher zu denken, aber teurer im Unterhalt
Ein zentraler, oft unterschätzter Grund für die Ice Cream Cone Verteilung ist eine kognitive Falle: Ein End to End Test ist einfacher zu schreiben, weil er einfach nachstellt, was ein Mensch am Bildschirm tun würde, Klick, Eingabe, Prüfung. Ein guter Unit Test dagegen verlangt, dass die zu testende Einheit überhaupt isolierbar ist, was oft ein Umbau der Architektur mit Dependency Injection oder sauberer Trennung von Geschäftslogik und Infrastruktur voraussetzt. Wer diesen Umbau scheut, weicht auf den Browser-Test aus, der über jede Architekturschwäche hinweg funktioniert, weil er die Anwendung als Blackbox behandelt.
Das Problem zeigt sich erst später: Der E2E-Test läuft langsam, reagiert empfindlich auf Timing-Probleme und Netzwerklatenz, und ein einziger fehlschlagender Selector-Wechsel legt dutzende Tests gleichzeitig lahm. Ein Unit Test für dieselbe Randfallprüfung liefe in Millisekunden und würde exakt zeigen, welche Berechnung falsch ist. Die kurzfristige Bequemlichkeit des E2E-Tests wird zur langfristigen Wartungslast der gesamten Suite. Das folgende Beispiel zeigt denselben Testfall einmal als aufwendigen Browser-Test und einmal als fokussierten Unit Test für dieselbe Geschäftsregel.
// BAD: business rule tested only through the full browser flow
test('applies 10% discount for orders over 100 EUR', async ({ page }) => {
await page.goto('/cart');
await page.getByTestId('add-item').click();
await page.getByTestId('qty-input').fill('5');
await page.getByTestId('checkout-btn').click();
await expect(page.getByTestId('discount-line')).toContainText('-10.00');
// Slow, flaky under load, and unclear which rule actually broke
});
// GOOD: same business rule isolated and tested in milliseconds
import { calculateDiscount } from '../src/pricing/discount';
test('applies 10% discount for orders over 100 EUR', () => {
const result = calculateDiscount({ subtotal: 150 });
expect(result.discountAmount).toBe(15);
expect(result.reason).toBe('volume_discount_10');
});
// The E2E flow itself still deserves one happy-path test, just not every rule
6. Testverteilung messen: Metriken statt Bauchgefühl
Ohne Zahlen bleibt die Diskussion über die Testpyramide subjektiv, jedes Teammitglied hat ein eigenes Bauchgefühl darüber, ob die Suite gesund ist. Ein einfaches, aber wirkungsvolles Skript zählt regelmäßig, wie viele Testdateien pro Ebene existieren und wie lange die jeweilige Ebene in der CI-Pipeline läuft. Diese Zahlen, über Zeit als Trend dargestellt, zeigen objektiv, ob sich die Verteilung verbessert oder verschlechtert, unabhängig davon, wie sich einzelne Entscheidungen im Review angefühlt haben.
Neben der reinen Anzahl ist die Flakiness-Rate pro Ebene eine ebenso wichtige Kennzahl: Ein Team, das feststellt, dass zwanzig Prozent der E2E-Tests gelegentlich grundlos fehlschlagen, während Unit Tests praktisch nie flaky sind, hat einen konkreten, datengestützten Grund, Logik von der Spitze der Pyramide in die Basis zu verschieben. Diese Metriken gehören in ein Dashboard, das im Sprint Review oder Retro sichtbar besprochen wird, statt in einem einmaligen Audit-Bericht zu verstauben, den niemand mehr öffnet.
#!/usr/bin/env bash
# scripts/test-distribution.sh - report the current pyramid shape
set -euo pipefail
unit_count=$(find tests/Unit -name '*Test.php' | wc -l)
integration_count=$(find tests/Integration -name '*Test.php' | wc -l)
e2e_count=$(find tests/e2e -name '*.spec.ts' | wc -l)
total=$((unit_count + integration_count + e2e_count))
printf 'Unit: %4d (%d%%)\n' "$unit_count" $((unit_count * 100 / total))
printf 'Integration: %4d (%d%%)\n' "$integration_count" $((integration_count * 100 / total))
printf 'E2E: %4d (%d%%)\n' "$e2e_count" $((e2e_count * 100 / total))
# Fail the pipeline if the pyramid inverts into an ice cream cone
if [ "$e2e_count" -gt "$unit_count" ]; then
echo "[WARN] More E2E tests than unit tests. Pyramid is inverted."
fi
7. Inkrementelle Migration von ungesund zu gesund
Ein Team, das eine bestehende Suite mit hunderten E2E-Tests und kaum Unit Tests vorfindet, sollte niemals versuchen, alles auf einmal umzuschreiben. Ein solches Vorhaben bindet Wochen an Kapazität, liefert währenddessen keinen fachlichen Mehrwert und wird in der Praxis fast immer nach der ersten dringenden Feature-Anfrage abgebrochen. Der wirksamere Weg folgt dem Strangler-Prinzip: Jede neue Funktion bekommt von Anfang an eine pyramidenkonforme Abdeckung, und bestehender Code wird nur dann migriert, wenn er ohnehin angefasst wird, etwa im Rahmen eines Bugfixes oder einer Erweiterung.
Damit diese Migration nicht beliebig verläuft, hilft ein grobes Zielverhältnis pro Quartal, das schrittweise angepasst wird, statt eines unrealistischen Sofort-Ziels. Ein Team, das aktuell bei fünf Prozent Unit-Test-Anteil steht, sollte sich nicht direkt siebzig Prozent zum Ziel setzen, sondern realistische Zwischenschritte definieren und diese im Sprint-Planning sichtbar mitverfolgen. Besonders wirksam ist es, bei jedem Bugfix in einem E2E-lastigen Bereich zusätzlich zur Fehlerbehebung einen fokussierten Unit Test für die konkrete Ursache zu ergänzen, wodurch Migration und Fehlerbehebung denselben Aufwand teilen, statt separate Arbeitspakete zu sein.
{
"test_pyramid_roadmap": {
"current_quarter": "2026-Q3",
"baseline": { "unit_pct": 12, "integration_pct": 18, "e2e_pct": 70 },
"targets": [
{ "quarter": "2026-Q3", "unit_pct": 25, "integration_pct": 25, "e2e_pct": 50 },
{ "quarter": "2026-Q4", "unit_pct": 45, "integration_pct": 30, "e2e_pct": 25 },
{ "quarter": "2027-Q1", "unit_pct": 60, "integration_pct": 25, "e2e_pct": 15 }
],
"migration_rule": "new_features_pyramid_compliant_by_default",
"opportunistic_migration": "add_unit_test_when_touching_e2e_covered_code"
}
}
8. Verantwortung und Ownership im Team verankern
Die Testpyramide bleibt fragil, solange Testverantwortung als Aufgabe einer separaten QA-Rolle verstanden wird, die am Ende der Entwicklung Tests nachträgt. Nachhaltiger ist ein Modell, in dem jeder Entwickler für die Tests seines eigenen Codes verantwortlich ist, inklusive der Entscheidung, auf welcher Ebene ein Test sinnvoll ist. Pairing zwischen erfahrenen und neuen Teammitgliedern beim Schreiben von Unit Tests vermittelt praktisches Können schneller als jede Dokumentation, weil die Entscheidung für eine Testebene live am konkreten Code nachvollzogen wird.
Eine rotierende Rolle als Testverantwortlicher, die nicht dauerhaft an eine Person gebunden ist, hilft zusätzlich, Testschulden regelmäßig in der Retro sichtbar zu machen, ohne dass Testqualität als Nebenaufgabe eines Einzelnen wahrgenommen wird. Wichtig ist, dass diese Rolle keine Verantwortung an sich zieht, die eigentlich im gesamten Team liegen sollte, sondern lediglich die Metriken aus Abschnitt sechs aufbereitet und zur Diskussion stellt. So bleibt die Testpyramide ein gemeinsames Anliegen des Teams, nicht die Zuständigkeit einer einzelnen Person, die bei Abwesenheit sofort wieder verwässert.
9. Testpyramide-Patterns im direkten Vergleich
Die folgende Übersicht fasst zusammen, welche konkreten Entscheidungen eine ungesunde Testverteilung begünstigen und welche Alternative sich in der Praxis als tragfähiger erwiesen hat, um die Pyramide tatsächlich im Alltag zu verankern.
| Situation | Ungesundes Pattern | Gesundes Pattern | Vorteil |
|---|---|---|---|
| Neue Geschäftslogik | Absicherung nur über einen E2E-Test | Unit Test plus ein E2E-Test für den Happy Path | Schnelles, präzises Feedback bei Regressionen |
| Code Review | "Test vorhanden" reicht als Kriterium | Explizite Frage nach der richtigen Testebene | Verteilung verschiebt sich PR für PR |
| Definition of Done | Vage Formulierung ohne Ebenenbezug | Ebenenspezifische Testanforderung pro Ticket-Typ | Überprüfbare, nicht verhandelbare Regel |
| Bestehende Suite umbauen | Großer Big-Bang-Rewrite-Sprint | Strangler-Migration bei jeder Berührung | Kein Stillstand, stetiger Fortschritt |
| Testverantwortung | Separate QA-Rolle am Ende der Pipeline | Entwickler besitzt Tests des eigenen Codes | Testebene wird direkt beim Schreiben entschieden |
Mironsoft
Testpyramide-Audits, CI-Gates und Testautomatisierung für Magento- und Hyvä-Teams
Eine Testpyramide, die im Alltag tatsächlich wirkt?
Wir analysieren eure bestehende Testsuite, bauen Code-Review-Checklisten und CI-Gates, die Pyramiden-Denken durchsetzen, und begleiten die schrittweise Migration von E2E-lastigen Suiten zu einer gesunden Testverteilung.
Testverteilung-Audit
Metriken zu Testebenen, Laufzeit und Flakiness aufbauen
CI-Gates einrichten
E2E-Budget, Pyramiden-Checks und Definition of Done verankern
Migrationsbegleitung
Strangler-Migration bestehender E2E-lastiger Suiten planen und umsetzen
10. Zusammenfassung
Die Testpyramide im Team etablieren heißt, sie aus dem Wiki heraus in konkrete, tägliche Entscheidungen zu übersetzen. Code Review, das gezielt nach der richtigen Testebene fragt, und eine Definition of Done, die zwischen Unit-, Integration- und E2E-Anforderungen unterscheidet, verändern das Verhalten eines Teams zuverlässiger als jede noch so gut geschriebene Dokumentation. Die E2E-Falle entsteht, weil Browser-Tests kurzfristig einfacher zu schreiben sind als isolierte Unit Tests, verursacht aber langfristig hohe Wartungskosten durch Flakiness und lange Laufzeiten.
Eine bestehende, ungesunde Verteilung lässt sich nicht über Nacht reparieren, aber über eine konsequente Strangler-Migration, bei der neue Funktionen pyramidenkonform entstehen und bestehender Code bei Berührung migriert wird, verschiebt sich die Verteilung über Quartale spürbar. Objektive Metriken statt Bauchgefühl, sichtbar im Sprint Review besprochen, und geteilte Verantwortung im gesamten Team statt einer isolierten QA-Rolle machen aus der Testpyramide eine gelebte Praxis statt einer Grafik im Wiki.
Testpyramide im Team etablieren, Das Wichtigste auf einen Blick
Code Review als Hebel
Explizit nach der niedrigsten sinnvollen Testebene fragen, statt nur zu prüfen, ob irgendein Test existiert.
Definition of Done
Ebenenspezifische Testanforderungen statt vager Formulierungen machen die Regel überprüfbar.
E2E-Falle vermeiden
Browser-Tests sind kurzfristig bequemer, aber langfristig teurer als saubere Unit Tests.
Inkrementelle Migration
Strangler-Prinzip statt Big-Bang-Rewrite, gestützt auf Metriken statt Bauchgefühl.