03 Quiz: Testing Grundlagen - Unit Testing und Clean Code
November 2025 (3020 Words, 17 Minutes)
Anleitung
Dieses Quiz testet dein Verständnis der Kernkonzepte aus Kapitel 03 (Testing-Grundlagen): Testing Grundlagen.
Warum dieses Quiz machen?
Für dein Lernen:
- Selbsteinschätzung: Identifiziere Bereiche, in denen du ein gutes Verständnis hast und Themen, die noch Wiederholung brauchen
- Prüfungsvorbereitung: Das Quizformat und die Schwierigkeit spiegeln wider, was du in der Abschlussprüfung erwarten kannst
- Sofortiges Feedback: Erhalte sofortiges Feedback zu deinen Antworten, um korrektes Verständnis zu verstärken
- Aktives Abrufen: Dich selbst zu testen ist eine der effektivsten Lerntechniken
Für Kursverbesserung:
- Anonymes Feedback: Deine Antworten helfen mir zu verstehen, welche Konzepte mehr Erklärung brauchen
- Inhaltsanpassung: Wenn viele Studierende mit einem Thema kämpfen, werden wir mehr Zeit darauf in der Vorlesung verwenden
- Tempo-Indikator: Zeigt mir, ob wir zu schnell oder zu langsam vorgehen
Richtlinien:
- Jede Frage hat 4 Optionen mit genau einer richtigen Antwort
- Versuche zuerst zu antworten, ohne die Vorlesungsmaterialien zu konsultieren
- Fokussiere auf Verständnis der Konzepte, nicht auf das Auswendiglernen spezifischer Zahlen
- Klicke auf “Antwort anzeigen”, um die Erklärung nach jedem Versuch zu sehen
Frage 1: Code Quality vs. Code Correctness
Deine CI-Pipeline zeigt alle Checks als bestanden an (Ruff, Pyright). Was sagt dir das über deinen Code?
A) Der Code ist produktionsbereit und fehlerfrei
B) Der Code folgt Style-Guidelines und hat korrekte Typen, kann aber trotzdem Logikfehler enthalten
C) Der Code wird alle Edge Cases korrekt behandeln
D) Der Code wurde gründlich getestet
Antwort anzeigen
Richtige Antwort: B
Code Quality Tools (Ruff, Pyright) prüfen Style und Typen, nicht Korrektheit. Sie verifizieren:
- ✅ Formatierung (PEP8-Konformität)
- ✅ Type Hints sind vorhanden und konsistent
- ✅ Ungenutzte Imports wurden entfernt
Sie verifizieren NICHT:
- ❌ Logik ist korrekt
- ❌ Edge Cases werden behandelt
- ❌ Funktionen geben korrekte Ergebnisse zurück
Deshalb brauchen wir Tests - um zu verifizieren, dass Code tatsächlich wie beabsichtigt funktioniert, nicht nur gut aussieht.
Frage 2: Die Testing Pyramid
Was ist die empfohlene Verteilung von Tests in der Testing Pyramid?
A) Gleichmäßige Verteilung: 33% Unit, 33% Modul, 33% End-to-End
B) Top-lastig: 10% Unit, 20% Modul, 70% End-to-End
C) Unten-lastig: 70% Unit, 20% Modul, 10% End-to-End
D) Mitten-lastig: 10% Unit, 80% Modul, 10% End-to-End
Antwort anzeigen
Richtige Antwort: C
Die Testing Pyramid empfiehlt:
- 70% Unit Tests (Basis der Pyramide) - Schnell, günstig, einfach zu debuggen
- 20% Modul/Integrationstests (Mitte) - Moderate Geschwindigkeit und Kosten
- 10% End-to-End Tests (Spitze) - Langsam, teuer, schwer zu debuggen
Warum diese Verteilung?
- Schnelle Feedback-Schleifen: Unit Tests laufen in Millisekunden, ermöglichen schnelle Iteration
- Einfaches Debuggen: Wenn ein Unit Test fehlschlägt, weißt du genau, welche Funktion defekt ist
- Wartbarkeit: Unit Tests sind einfacher zu schreiben und zu warten als E2E Tests
- Kosteneffektiv: 50 Unit Tests laufen schneller als 5 E2E Tests
Die Pyramidenform spiegelt sowohl Geschwindigkeit als auch Menge wider - mehr Tests auf der schnellen, günstigen Ebene.
Frage 3: Das Test Cone Anti-Pattern
Was ist der “Test Cone” (invertierte Pyramide) und warum ist er problematisch?
A) Eine Testing-Strategie, die sich auf Integrationstests fokussiert, was der effizienteste Ansatz ist
B) Ein Muster, bei dem die meisten Tests langsame End-to-End Tests sind, was zu langsamem Feedback und brüchigen Tests führt
C) Ein moderner Testing-Ansatz, der die veraltete Testing Pyramid ersetzt
D) Eine Methode, um Testdateien in Verzeichnissen zu organisieren
Antwort anzeigen
Richtige Antwort: B
Der Test Cone ist ein Anti-Pattern, bei dem die Testing Pyramid invertiert ist:
- Viele E2E Tests (Spitze der Pyramide wird breit)
- Wenige oder keine Unit Tests (Basis der Pyramide wird schmal)
Probleme mit dem Test Cone:
- Langsames Feedback: Jede Änderung erfordert das Ausführen langsamer E2E Tests (Minuten, nicht Sekunden)
- Schwer zu debuggen: Wenn E2E Test fehlschlägt, welches der 10 Module hat es verursacht?
- Brüchig: UI-Änderungen brechen viele E2E Tests
- Teufelskreis: Tests sind langsam → Entwickler vermeiden es, sie auszuführen → Bugs häufen sich an
Wie es passiert:
- “Ich weiß nicht, wie man Unit Tests schreibt”
- “Die UI ist einfach manuell durchzuklicken”
- “Unit Tests sind zu viel Arbeit”
Lösung: Lerne Unit Tests richtig zu schreiben (diese Vorlesung!) und baue die Pyramide korrekt auf.
Frage 4: Unit Test Eigenschaften
Welches der folgenden ist KEINE Eigenschaft eines guten Unit Tests?
A) Testet eine einzelne Funktion oder Methode isoliert
B) Erfordert das Starten der gesamten Anwendung (Dash, Datenbank, etc.)
C) Läuft in Millisekunden
D) Hat klare Erwartungen (Arrange-Act-Assert Muster)
Antwort anzeigen
Richtige Antwort: B
Gute Unit Tests sollten:
- ✅ Isoliert testen: Eine Funktion/Methode, keine Abhängigkeiten von anderen Systemen
- ✅ Schnell sein: Laufen in Millisekunden (nicht Sekunden oder Minuten)
- ✅ Klar sein: Einfach zu verstehen, was getestet wird
- ✅ Unabhängig sein: Können in jeder Reihenfolge ausgeführt werden
Unit Tests sollten NICHT:
- ❌ Die gesamte Anwendung starten
- ❌ Sich mit Datenbanken, APIs oder externen Services verbinden
- ❌ Manuelles Setup erfordern (wie durch die UI klicken)
- ❌ Von anderen Tests abhängen, die zuerst ausgeführt werden
Beispiel:
# ✅ Guter Unit Test - testet find_intersection() isoliert
def test_find_intersection_normal_angle():
x, y, dist = find_intersection(test_data)
assert dist > 0
# ❌ Kein Unit Test - erfordert Start der Dash App
def test_full_application_flow():
app = start_dash_app() # Zu langsam!
response = app.click_button()
assert response.status == 200
Frage 5: Das AAA Pattern
Wofür steht das AAA Pattern beim Unit Testing?
A) Always Assert Accurately (Immer genau prüfen)
B) Arrange-Act-Assert (Vorbereiten-Ausführen-Prüfen)
C) Analyze-Apply-Approve (Analysieren-Anwenden-Genehmigen)
D) Automatic-Assertion-Analysis (Automatische-Assertions-Analyse)
Antwort anzeigen
Richtige Antwort: B
Das AAA Pattern ist eine Standardstruktur für Unit Tests:
- Arrange (Vorbereiten): Testdaten und Bedingungen einrichten
- Act (Ausführen): Die zu testende Funktion aufrufen
- Assert (Prüfen): Verifizieren, dass das Ergebnis den Erwartungen entspricht
Beispiel:
def test_find_intersection_normal_angle():
# ARRANGE: Testdaten einrichten
x_road = np.array([0, 10, 20, 30])
y_road = np.array([0, 2, 4, 6])
angle = -10.0
# ACT: Funktion aufrufen
x, y, dist = find_intersection(x_road, y_road, angle)
# ASSERT: Ergebnis verifizieren
assert x is not None
assert dist > 0
Warum dieses Pattern?
- Lesbarkeit: Klare Struktur macht Tests einfach zu verstehen
- Debugging: Einfach zu sehen, was getestet wird und was fehlgeschlagen ist
- Konsistenz: Alle Tests folgen dem gleichen Muster
Frage 6: Exhaustives Testing
Warum ist exhaustives Testing (Testen aller möglichen Eingaben) für die meisten Funktionen unmöglich?
A) Weil Computer zu langsam sind, um so viele Tests auszuführen
B) Weil die Anzahl möglicher Eingabekombinationen exponentiell wächst und es unpraktisch macht
C) Weil wir nicht genug Festplattenspeicher haben, um alle Tests zu speichern
D) Weil Testing-Frameworks Limits für die Anzahl der Tests haben
Antwort anzeigen
Richtige Antwort: B
Exhaustives Testing ist unmöglich wegen:
- Kombinatorische Explosion: Jeder Parameter multipliziert die Anzahl der Testfälle
- Unendliche oder nahezu unendliche Eingaberäume: Floats, Strings, Arrays haben zu viele mögliche Werte
- Zeitbeschränkungen: Testen aller Kombinationen würde länger dauern als das Alter des Universums
Beispiel: Eine Funktion mit nur 3 Float-Parametern:
- Jeder Parameter: ~1.000.000 mögliche Werte (konservative Schätzung)
- Gesamtkombinationen: 1.000.000³ = 1 Trillion Tests
- Zeit bei 1ms pro Test: ~32 Milliarden Jahre
Was wir stattdessen tun:
- Intelligente Testing-Strategien: Äquivalenzklassen, Grenzwertanalyse (Kapitel 03 (Grenzwertanalyse))
- Risikobasiertes Testing: Fokus auf kritische Funktionalität
- Repräsentative Stichproben: Wähle Eingaben, die verschiedene Szenarien repräsentieren
Kernaussage: Du kannst nie alles testen, daher musst du strategisch vorgehen, was du testest.
Frage 7: pytest Vorteile
Was ist der HAUPTVORTEIL von pytest gegenüber manuellem Testing (z.B. print Statements)?
A) pytest lässt Tests schneller laufen
B) pytest entdeckt, organisiert und berichtet Testergebnisse automatisch mit detaillierten Fehlerinformationen
C) pytest reduziert die Anzahl der Tests, die du schreiben musst
D) pytest eliminiert die Notwendigkeit, Assertions zu schreiben
Antwort anzeigen
Richtige Antwort: B
pytest bietet Automatisierung und Organisation für Testing:
Mit pytest:
- ✅ Auto-Discovery: Findet alle
test_*.pyDateien automatisch - ✅ Umfangreiche Ausgabe: Zeigt genau, welcher Test fehlgeschlagen ist, Zeilennummer, Fehlertyp
- ✅ Organisierte Berichte: Klare Pass/Fail Zählung, Ausführungszeit
- ✅ Test-Isolation: Jeder Test läuft unabhängig
- ✅ CI/CD Integration: Funktioniert nahtlos mit GitHub Actions
- ✅ Einfache Syntax: Nutze einfach
assert(keine speziellen Methoden nötig)
Ohne pytest (manuelles Testing):
- ❌ Muss jede Testfunktion manuell aufrufen
- ❌ Bekommt nur print Statements (keine strukturierte Ausgabe)
- ❌ Keine klare Zusammenfassung, was bestanden/fehlgeschlagen ist
- ❌ Muss eigene Fehlerbehandlung schreiben
- ❌ Tests könnten sich gegenseitig beeinflussen
Beispielvergleich:
# Manuell - primitiv und mühsam
def manual_test():
if result != expected:
print("FAILED")
return False
return True
# pytest - sauber und leistungsstark
def test_something():
assert result == expected # pytest erledigt den Rest!
Frage 8: Ein Konzept pro Test
Warum ist es wichtig, nur EIN Konzept pro Testfunktion zu testen?
A) Um Tests schneller laufen zu lassen, indem die Menge an Code reduziert wird
B) Um Speicher zu sparen durch weniger Variablen
C) Um sofort klar zu machen, was kaputt ist, wenn ein Test fehlschlägt
D) Um Python Namenskonventionen zu folgen
Antwort anzeigen
Richtige Antwort: C
Ein Konzept pro Test bedeutet, dass jeder Test ein einzelnes Verhalten oder Szenario verifiziert.
Vorteile:
- ✅ Klare Fehler: Testname sagt dir genau, was kaputt ist
- ✅ Einfaches Debugging: Keine Notwendigkeit, den ganzen Test zu lesen, um das Problem zu finden
- ✅ Bessere Organisation: Verwandte Tests sind gruppiert aber getrennt
- ✅ Einfachere Wartung: Kann ein Konzept modifizieren/entfernen ohne andere zu beeinflussen
Beispiel einer Verletzung:
# ❌ Schlecht: Mehrere Konzepte in einem Test
def test_find_intersection_everything():
# Konzept 1: Downward Angle
assert find_intersection(..., -10.0)[0] is not None
# Konzept 2: Vertical Angle
assert find_intersection(..., 90.0)[0] is None
# Konzept 3: Leere Arrays
assert find_intersection([], [], -10.0)[0] is None
Wenn dieser Test fehlschlägt, welches Konzept ist kaputt? Du musst untersuchen.
Besserer Ansatz:
# ✅ Gut: Ein Konzept pro Test
def test_find_intersection_finds_intersection_for_downward_angle():
assert find_intersection(..., -10.0)[0] is not None
def test_find_intersection_returns_none_for_vertical_angle():
assert find_intersection(..., 90.0)[0] is None
def test_find_intersection_returns_none_for_empty_arrays():
assert find_intersection([], [], -10.0)[0] is None
Wenn jetzt ein Test fehlschlägt: Testname sagt dir sofort, welches Szenario kaputt ist!
Frage 9: Beschreibende Testnamen
Welcher Testname folgt dem empfohlenen Muster: test_[funktion]_[szenario]_[erwartetes_verhalten]()?
A) test_1()
B) test_intersection()
C) test_find_intersection_returns_none_for_empty_arrays()
D) test_edge_case()
Antwort anzeigen
Richtige Antwort: C
Gute Testnamen sind beschreibend und folgen einem Muster:
test_[funktion]_[szenario]_[erwartetes_verhalten]()
Analyse jeder Option:
A) test_1() - ❌ Sagt dir nichts
- Welche Funktion? Welches Szenario? Was wird erwartet?
- Nutzlos wenn Test fehlschlägt: “test_1 failed” bedeutet nichts
B) test_intersection() - ❌ Zu vage
- Welche Funktion? Welches Szenario?
- Nicht spezifisch genug, um zu verstehen, was fehlgeschlagen ist
C) test_find_intersection_returns_none_for_empty_arrays() - ✅ Perfekt!
- Funktion:
find_intersection - Szenario: leere Arrays
- Erwartetes Verhalten: gibt None zurück
- Wenn es fehlschlägt: “Empty Array Handling ist kaputt” - sofort klar!
D) test_edge_case() - ❌ Nicht spezifisch
- Welcher Edge Case?
- Welche Funktion?
Reale Auswirkung:
# Unhilfreiche Fehlermeldung:
❌ FAILED test_1
# Hilfreiche Fehlermeldung:
✅ FAILED test_find_intersection_returns_none_for_empty_arrays
# Du weißt sofort: "Oh, Empty Array Handling ist kaputt!"
Frage 10: Feedback Loop Geschwindigkeit
Warum ist die Geschwindigkeit von Tests wichtig für die Entwicklungs-Feedback-Schleife?
A) Schnellere Tests verbrauchen weniger Strom
B) Schnelle Tests können während der Entwicklung häufig ausgeführt werden und fangen Bugs sofort
C) Schnelle Tests benötigen weniger leistungsstarke Computer
D) Geschwindigkeit ist nur in CI/CD wichtig, nicht bei lokaler Entwicklung
Antwort anzeigen
Richtige Antwort: B
Schnelle Tests ermöglichen schnelle Iteration:
Mit schnellen Unit Tests (Millisekunden):
- ✅ Tests nach jeder Codeänderung ausführen
- ✅ Bugs sofort fangen (innerhalb von Sekunden)
- ✅ Im Flow-Zustand bleiben (kein Warten)
- ✅ Bugs fixen während Kontext frisch ist
- ✅ Entwickler führen Tests tatsächlich aus (werden nicht vermieden)
Mit langsamen E2E Tests (Minuten):
- ❌ Tests selten ausführen (zu langsam)
- ❌ Bugs häufen sich zwischen Testläufen an
- ❌ Kontextwechsel während des Wartens (Kaffee holen, E-Mails checken)
- ❌ Bugs Stunden später fixen (vergessen was geändert wurde)
- ❌ Entwickler vermeiden Tests (zu langsam)
Realer Vergleich:
Ändere eine Zeile in find_intersection()
Schnelle Feedback-Schleife:
Code schreiben → 50 Unit Tests ausführen (0.5s) → Sofortiges Ergebnis
→ Bei Bedarf fixen → Schnell iterieren
Langsame Feedback-Schleife:
Code schreiben → 50 E2E Tests ausführen (250s) → 4+ Minuten warten
→ Abgelenkt werden → Kontext verlieren → Schwerer zu debuggen
Kernprinzip: Je schneller das Feedback, desto produktiver der Entwickler.
Frage 11: Mehrere Asserts in einem Test
Wann ist es akzeptabel, mehrere assert Statements in einem einzelnen Test zu haben?
A) Nie - immer in separate Tests aufteilen
B) Wenn sie verschiedene Aspekte desselben Konzepts testen
C) Wenn du Zeit sparen willst, indem du Tests kombinierst
D) Nur wenn mehr als drei Dinge getestet werden
Antwort anzeigen
Richtige Antwort: B
Mehrere Asserts sind akzeptabel, wenn sie dasselbe Konzept testen:
✅ Gut: Mehrere Asserts für ein Konzept (Koordinatengültigkeit)
def test_find_intersection_returns_valid_coordinates():
"""Test dass Koordinaten innerhalb erwarteter Grenzen sind."""
x, y, dist = find_intersection(...)
# Alle verifizieren dasselbe Konzept: gültige Koordinatengrenzen
assert x is not None
assert y is not None
assert 0 <= x <= 30
assert 0 <= y <= 6
Warum das OK ist: Alle Asserts prüfen “Koordinatengültigkeit” - ein Konzept mit mehreren Aspekten.
❌ Schlecht: Mehrere Asserts für verschiedene Konzepte
def test_find_intersection_coordinates_and_distance():
x, y, dist = find_intersection(...)
assert x > 0 # Konzept 1: x Position
assert dist > 0 # Konzept 2: Distanz (unterschiedlich!)
Warum das schlecht ist: Testet zwei unabhängige Konzepte (Position und Distanz).
Richtlinie:
- Gleiches Konzept (Koordinatengültigkeit, Fehlerbehandlung) → Mehrere Asserts OK
- Verschiedene Konzepte (Position vs. Distanz, Input vs. Output) → Tests aufteilen
Teste dich selbst: Wenn du nicht beschreiben kannst, was der Test prüft in einem Satz, teile ihn auf.
Frage 12: Test-Isolation
Was bedeutet “Test-Isolation” und warum ist sie wichtig?
A) Tests sollten in separaten Ordnern ausgeführt werden
B) Jeder Test sollte unabhängig laufen ohne von anderen Tests oder externen Systemen abhängig zu sein
C) Tests sollten von der Hauptcodebasis isoliert sein
D) Tests sollten nur auf isolierten CI-Servern laufen
Antwort anzeigen
Richtige Antwort: B
Test-Isolation bedeutet, dass jeder Test:
- ✅ Unabhängig läuft (hängt nicht von anderen Tests ab)
- ✅ In jeder Reihenfolge laufen kann
- ✅ Keine Seiteneffekte hat (modifiziert keinen globalen Zustand)
- ✅ Nicht von externen Systemen abhängt (Datenbanken, APIs, UI)
Warum Isolation wichtig ist:
1. Parallele Ausführung:
# Isolierte Tests können parallel laufen
$ pytest -n auto # Führt Tests gleichzeitig aus
2. Zuverlässiges Debugging:
# ✅ Isoliert: Kann nur diesen einen Test ausführen
$ pytest tests/test_geometry.py::test_find_intersection_normal_angle
# ❌ Nicht isoliert: Muss erst test_setup() ausführen
# Kann nicht isoliert debuggen
3. Keine instabilen Tests:
# ❌ Nicht isoliert - hängt von Testreihenfolge ab
def test_a():
global_state = 5 # Setzt globalen Zustand
def test_b():
assert global_state == 5 # Schlägt fehl wenn test_a nicht zuerst läuft!
# ✅ Isoliert - jeder Test setzt seine eigenen Daten
def test_a():
local_state = 5
assert local_state == 5
def test_b():
local_state = 10
assert local_state == 10
Wie pytest hilft:
- Jede Testfunktion läuft in ihrem eigenen Scope
- Kein geteilter Zustand zwischen Tests
- Tests können in jeder Reihenfolge laufen (oder parallel)
Bewertungsrichtlinie
- 10-12 richtig: Ausgezeichnet! Du hast ein gutes Verständnis der Testing-Grundlagen und Clean Code Prinzipien für Tests
- 7-9 richtig: Gutes Verständnis! Wiederhole die Bereiche, die du verpasst hast, um dein Wissen zu festigen
- 4-6 richtig: Ordentliches Verständnis. Wiederhole die Vorlesungsmaterialien und fokussiere auf die Testing Pyramid, AAA Pattern und Clean Code Prinzipien
- Unter 4: Bitte wiederhole die Vorlesung sorgfältig und melde dich in der Sprechstunde, wenn du Klärung brauchst
Wichtige Erkenntnisse zum Merken
- Code Quality ≠ Code Correctness - CI prüft Style, Tests prüfen Logik
- Testing Pyramid: 70% Unit, 20% Modul, 10% E2E - Schnelle Tests an der Basis
- Test Cone ist ein Anti-Pattern - Invertierte Pyramide führt zu langsamen, brüchigen Tests
- AAA Pattern: Arrange-Act-Assert - Standardstruktur für lesbare Tests
- Exhaustives Testing ist unmöglich - Muss intelligente Testing-Strategien verwenden
- pytest bietet Automatisierung - Auto-Discovery, umfangreiche Ausgabe, CI/CD Integration
- Ein Konzept pro Test - Klare Fehler, einfaches Debugging
- Beschreibende Testnamen -
test_[funktion]_[szenario]_[erwartet]() - Schnelle Feedback-Schleifen sind wichtig - Unit Tests ermöglichen schnelle Iteration
- Test-Isolation ist kritisch - Unabhängige Tests können in jeder Reihenfolge laufen
- Mehrere Asserts OK für gleiches Konzept - Aber verschiedene Konzepte aufteilen
- Unit Tests testen isoliert - Keine Abhängigkeiten von externen Systemen
Was kommt als Nächstes?
Kapitel 03 (Grenzwertanalyse) wird behandeln:
- ✅ Äquivalenzklassen-Partitionierung (intelligente Testing-Strategien)
- ✅ Grenzwertanalyse (Edge Cases finden)
- ✅ LLM-unterstütztes Test-Schreiben (AI nutzen, um Testing zu beschleunigen)
- ✅ Mensch-AI Kollaboration für Testing
Übe vorerst:
- Schreibe Unit Tests für einfache Funktionen
- Folge dem AAA Pattern
- Verwende beschreibende Testnamen
- Halte Tests isoliert und fokussiert
Probiere das: Nimm eine Funktion, die du geschrieben hast, und schreibe 3 Unit Tests dafür mit dem, was du heute gelernt hast!