Alle Tabs der Lerneinheit (Erklärung · Interaktiv verstehen · Praxis-Übung · Klausur-Quiz) als durchgehender Text. Ideal zum Wiederholen vor der Klausur, und für Suchmaschinen wie Google, Bing und KI-Suche (ChatGPT, Perplexity).
Erklärung
Ungetesteter Code ist gefährlicher als kein Code. Tests sind die Versicherung gegen Regressionen, sie sagen dir SOFORT, wenn deine Änderung etwas kaputt macht.
Die Idee in einem Satz
Test-Pyramide (Mike Cohn 2009): Strategie für die Verteilung verschiedener Test-Arten, viele schnelle Unit-Tests, weniger Integrations-Tests, ganz wenig E2E-Tests.
Die Pyramide
/\
/ \
/E2E\ ← Wenige, langsame, fragile (5-10 %)
/------\
/ \
/Integration\ ← Mehr (15-25 %)
/------------\
/ \
/ Unit \ ← Viele, schnelle, robust (70-80 %)
/-------------------\
Faustregel: Pro Ebene 5-10× weniger Tests als die Ebene darunter.
Die 3 Test-Ebenen
1. Unit-Tests (Basis der Pyramide)
Was? Eine einzelne Funktion/Methode/Klasse in Isolation testen.
@Test
void einzahlen_erhoeht_kontostand() {
// Arrange
Konto k = new Konto(100.0);
// Act
k.einzahlen(50.0);
// Assert
assertEquals(150.0, k.getKontostand());
}
Charakteristik:
- Schnell (Millisekunden)
- Isoliert (keine DB, kein Netzwerk, keine Filesystem)
- Viele (oft 1000+ pro Projekt)
- Mocking für Abhängigkeiten (z.B. Mock-DB)
Frameworks: JUnit (Java), pytest (Python), Mocha/Vitest (JS), xUnit (.NET).
2. Integrations-Tests (Mitte)
Was? Mehrere Komponenten/Module zusammen testen, z.B. mit echter DB, echter API.
@Test
void bestellung_speichern_persistiert_in_db() {
// Arrange
BestellungRepository repo = new BestellungRepository(dataSource);
Bestellung b = new Bestellung("kunde-1", List.of(...));
// Act
repo.save(b);
// Assert
Bestellung gespeichert = repo.findById(b.getId());
assertEquals(b.getKunde(), gespeichert.getKunde());
}
Charakteristik:
- Langsamer (Sekunden)
- Echte Abhängigkeiten (DB läuft, API erreichbar)
- Weniger (z.B. 100-200 pro Projekt)
- Stateful (DB-Zustand muss aufgeräumt werden)
Frameworks: Spring Boot Test (Java), Django Test (Python), Testcontainers (für DB-Container).
3. E2E-Tests (Spitze)
Was? Komplette User-Journeys durch die gesamte Anwendung, Browser + UI + Backend + DB.
test('user kann sich registrieren und einloggen', async ({ page }) => {
await page.goto('https://shop.example.com');
await page.click('text=Registrieren');
await page.fill('#email', 'test@example.com');
await page.fill('#password', 'sicher123');
await page.click('button:has-text("Registrieren")');
await expect(page).toHaveURL(/.*\/dashboard/);
});
Charakteristik:
- Sehr langsam (Minuten)
- Fragil (UI-Änderungen brechen Tests, Netzwerk-Probleme)
- Wenige (z.B. 10-30 pro Projekt)
- Wirklichkeitsnah (testet wie Nutzer wirklich klickt)
Frameworks: Selenium, Cypress, Playwright.
Vergleich der 3 Ebenen
| Aspekt | Unit | Integration | E2E |
|---|---|---|---|
| Geschwindigkeit | ms | s | min |
| Anzahl | sehr viele | mittel | wenige |
| Isolation | hoch | mittel | keine |
| Wartungsaufwand | gering | mittel | sehr hoch |
| Falsch-positiv-Rate | gering | mittel | hoch (flaky tests) |
| Bug-Findung pro Test | gering | mittel | hoch |
| Debug-Aufwand bei Fehler | gering | mittel | hoch |
AAA-Pattern (Arrange-Act-Assert)
Goldstandard für Test-Struktur:
@Test
void test_method_name() {
// Arrange: Vorbedingungen
Konto k = new Konto(100.0);
// Act: die zu testende Aktion
k.einzahlen(50.0);
// Assert: Erwartetes Ergebnis prüfen
assertEquals(150.0, k.getKontostand());
}
FIRST-Prinzipien für gute Tests
| Buchstabe | Prinzip |
|---|---|
| Fast | Schnell, sonst werden Tests nicht ausgeführt |
| Independent | Unabhängig, Tests dürfen keine Reihenfolge brauchen |
| Repeatable | Wiederholbar, gleiches Ergebnis in jeder Umgebung |
| Self-Validating | Selbst-prüfend, eindeutig PASS/FAIL, kein manueller Check |
| Timely | Rechtzeitig, am besten VOR oder MIT dem Code (TDD) |
Test Doubles
Wenn echte Abhängigkeiten zu langsam/komplex sind:
| Typ | Was tut er? |
|---|---|
| Dummy | Wird gar nicht benutzt, nur zum Befüllen von Parametern |
| Stub | Gibt vorgegebene Antworten zurück (z.B. immer "Sonne" als Wetter) |
| Mock | Wie Stub, prüft aber auch ob er aufgerufen wurde |
| Spy | Wie Mock, ruft echte Methode + zeichnet auf |
| Fake | Eigene einfache Implementierung (z.B. In-Memory-DB) |
Test-Coverage
Code-Coverage: Prozent der Code-Zeilen, die von Tests durchlaufen werden.
- 80 % Coverage ist typisches Ziel
- 100 % ist meistens zu teuer / nicht sinnvoll
- Coverage allein ist NICHT genug, 100 % Coverage mit schlechten Assertions ist wertlos
Klausur-Faustregeln
1. Test-Pyramide: Unten viele Unit-Tests, oben wenige E2E.
2. Unit-Test: Eine Funktion isoliert. Mocks für Abhängigkeiten.
3. Integration: Mehrere Komponenten zusammen, echte DB.
4. E2E: Komplette User-Journey via Browser.
5. AAA: Arrange / Act / Assert.
6. FIRST: Fast / Independent / Repeatable / Self-Validating / Timely.
Häufige Stolpersteine
1. Test-Eis-Hut statt Pyramide. Wenn man unten WENIGE Unit-Tests und oben VIELE E2E-Tests hat (umgekehrte Pyramide), ist die Test-Suite langsam, fragil und teuer in der Wartung. Anti-Pattern.
2. Mocks überall. Wenn man jede Abhängigkeit mockt, testet man im Endeffekt die Mocks, nicht den Code. Faustregel: möglichst wenig mocken, am liebsten echte Implementierungen.
3. Tests von externer Reihenfolge abhängig. Test A muss vor Test B laufen, sonst schlägt B fehl. Das verletzt FIRST (Independent). Jeder Test muss eigenständig laufen können.
4. Test-Coverage als Goldstandard. 100 % Coverage mit schlechten Tests sagt nichts aus. Wichtiger als Coverage: testen die Tests die WICHTIGSTEN Pfade und Edge Cases?
5. Flaky Tests ignorieren. Tests, die manchmal grün, manchmal rot sind, sind ein RIESEN-Problem. Entweder Bug im Test oder im Code. NICHT einfach "retry" einbauen.
Interaktiv verstehen
Test-Pyramide interaktiv
Verschiebe die Test-Verteilung und sieh die Auswirkungen auf Geschwindigkeit, Wartung und Fehler-Findung.
Interaktive Visualisierung
Interaktive Komponente: probiere sie im Topic-Player oben aus.
Klausur-Tipp: Bei der Frage "Wie testet man X?" immer fragen: Welche Ebene macht hier am meisten Sinn? Reine Logik (Berechnungen, Validierung) → Unit. Datenbank-Zugriff → Integration. Komplette User-Flow → E2E. Mische die Ebenen, aber halt die Pyramide ein.
Praxis-Übung
Testen, Praxis-Übung
6 Aufgaben zu Test-Ebenen, AAA-Pattern und Test Doubles.
Klausurfragen mit Lösungen (6)
- F1.Welche Test-Ebene gehört in die BASIS der Test-Pyramide?
Antwort: Unit-Tests
Erklärung: Die BASIS der Pyramide besteht aus Unit-Tests (70-80 % aller Tests). Sie sind schnell, isoliert, robust. Darüber kommen Integrations-Tests (15-25 %), an der Spitze die E2E-Tests (5-10 %). Die Form 'breit unten, schmal oben' garantiert schnelle Tests und niedrigen Wartungs-Aufwand.
- F2.Was steht hinter dem AAA-Pattern?
Antwort: Arrange, Act, Assert
Erklärung: AAA = Arrange / Act / Assert. Standard-Struktur eines Tests: 1) Arrange = Vorbedingungen schaffen (Test-Daten, Mocks), 2) Act = die zu testende Methode aufrufen, 3) Assert = erwartetes Ergebnis prüfen. Diese Struktur macht Tests lesbar und konsistent.
- F3.Ordne die Test-Doubles ihren Eigenschaften zu.
Zuordnungen:
- Stub → Gibt vorgegebene Antworten zurück
- Mock → Wie Stub + prüft, ob aufgerufen wurde
- Fake → Eigene einfache Implementierung (z.B. In-Memory-DB)
- Dummy → Wird gar nicht benutzt, nur Parameter-Füller
Erklärung: Die 4 wichtigsten Test-Doubles (nach Meszaros, xUnit Patterns): Dummy = wird nicht benutzt. Stub = liefert feste Antworten. Mock = Stub + prüft Aufrufe. Fake = eigene einfache Implementierung. Spy = wie Mock + ruft echte Methode auf. Wichtig in Klausuren: Mock vs Stub-Unterschied.
Typ: Zuordnung
- F4.Welche Tests sind typischerweise am SCHNELLSTEN auszuführen?
Antwort: Unit-Tests mit Mocks
Erklärung: Unit-Tests mit Mocks sind am schnellsten, Millisekunden pro Test, da keine echten DB-Zugriffe, kein Netzwerk, kein Filesystem. Integration-Tests dauern Sekunden (echte DB-Operationen), E2E sogar Minuten (Browser starten, Seiten laden). Daher: Unit-Tests in jedem CI-Lauf, Integration nur bei Pull-Requests, E2E vielleicht nur in Nightly-Builds.
- F5.100 % Code-Coverage bedeutet, dass alle Bugs gefunden werden.
Antwort: Falsch
Erklärung: FALSCH. Coverage misst nur, welche Code-Zeilen durchlaufen werden, NICHT ob die Assertions sinnvoll sind. Man kann 100 % Coverage haben mit Tests wie `assertTrue(true)`, die nichts wirklich prüfen. Außerdem werden Edge Cases (Null-Werte, Grenz-Werte) oft nicht durch Coverage erfasst. Mutation Testing ist eine bessere Metrik.
Typ: Wahr/Falsch
- F6.Du möchtest die Funktion berechneRabatt(Kunde, Bestellung) testen, die intern den UserService und ProductService aufruft. Welche Test-Strategie ist optimal?
Antwort: Unit-Test mit gemockten Services
Erklärung: Unit-Test mit Mocks ist optimal: schnell, isoliert, fokussiert auf die Rabatt-LOGIK ohne Service-Abhängigkeiten. Du mockst `UserService` und `ProductService` mit gewünschten Test-Daten und prüfst, ob `berechneRabatt` korrekt rechnet. Integration-Test wäre Overkill für reine Berechnungs-Logik. E2E wäre extreme Verschwendung.
Klausur-Quiz
Testen, Klausur-Quiz
6 typische Klausurfragen.
Klausurfragen mit Lösungen (6)
- F1.Wer hat die Test-Pyramide popularisiert?
Antwort: Mike Cohn
Erklärung: Mike Cohn beschrieb die Test-Pyramide 2009 in seinem Buch 'Succeeding with Agile'. Robert C. Martin schrieb über Clean Code, Martin Fowler über Refactoring, Kent Beck über TDD und XP. Alle relevant in der Test-Welt, aber Test-Pyramide = Cohn.
- F2.Welches Verhältnis schlägt die Test-Pyramide ungefähr vor?
Antwort: Viele Unit, weniger Integration, ganz wenig E2E (~70/20/10)
Erklärung: Klassische Test-Pyramide: ~70-80 % Unit, ~15-25 % Integration, ~5-10 % E2E. Untere Ebene MUSS am breitesten sein, sonst wird die Test-Suite zu langsam und zu fragil. Wenn dein Profil umgekehrt aussieht (Eis-Hut statt Pyramide), hast du ein Problem.
- F3.Tests sollten dem {{1}}-Prinzip folgen: Fast, Independent, Repeatable, Self-Validating, Timely. Der unteren Ebene der Test-Pyramide entspricht {{2}}-Tests, der obersten {{3}}-Tests.
Lösungen pro Lücke:
- {{1}}: FIRST
- {{2}}: Unit
- {{3}}: E2E / End-to-End
Erklärung: FIRST = Fast (schnell), Independent (unabhängig), Repeatable (wiederholbar), Self-Validating (eindeutig pass/fail), Timely (rechtzeitig, am besten TDD). Test-Pyramide-Ebenen: Unit (Basis), Integration (Mitte), E2E/End-to-End (Spitze). Klausur-Klassiker.
Typ: Lückentext
- F4.Bringe die Test-Schritte des AAA-Patterns + ein typisches Test-Setup in die richtige Reihenfolge.
Richtige Reihenfolge:
- Arrange: Test-Daten + Mocks setup
- Act: Methode unter Test aufrufen
- Assert: Erwartetes Ergebnis prüfen
- Aufräumen: DB-State zurücksetzen (falls nötig)
Erklärung: Reihenfolge: 1) Arrange (Test-Daten erstellen, Mocks konfigurieren), 2) Act (zu testende Methode aufrufen, meist 1 Zeile), 3) Assert (Erwartetes Ergebnis prüfen, meistens mit assertEquals/assertTrue), 4) Aufräumen (z.B. DB-State zurücksetzen, in @AfterEach). Letzter Schritt wichtig für Independent-FIRST-Prinzip.
Typ: Reihenfolge
- F5.Was ist der HAUPT-Unterschied zwischen einem Mock und einem Stub?
Antwort: Mocks prüfen, ob sie aufgerufen wurden (verify), Stubs nur Antworten geben
Erklärung: Stub LIEFERT vordefinierte Antworten. Mock TUT DAS AUCH, prüft aber ZUSÄTZLICH, ob/wie er aufgerufen wurde (z.B. `verify(mockUserService).getUser('id-1')`). Praxis-Tipp: Mockito unterscheidet das nicht streng, viele Frameworks haben beide in einem. Aber konzeptionell: Mock = mit Aufruf-Verifikation, Stub = nur Antworten.
- F6.E2E-Tests werden bevorzugt, weil sie die realistischste Test-Form sind, daher sollten sie die Mehrheit der Tests ausmachen.
Antwort: Falsch
Erklärung: FALSCH. E2E-Tests sind zwar realistisch, aber LANGSAM (Minuten pro Test), FRAGIL (UI-Änderung bricht Test), AUFWENDIG IN DER WARTUNG. Sie sollten die MINDERHEIT der Tests sein (5-10 %). Die Mehrheit (70-80 %) sind Unit-Tests, die schnell, robust und fokussiert sind. Eine Test-Suite, die hauptsächlich aus E2E besteht (Eis-Hut), ist ein Anti-Pattern.
Typ: Wahr/Falsch