03 Grundlagen des Testens: Coverage Konzepte Quiz
December 2025 (6108 Words, 34 Minutes)
Neu: Interaktives Quiz verfügbar!
Möchtest du deinen Fortschritt verfolgen und sofortiges Feedback erhalten? Probiere unsere neue interaktive Version mit Fortschrittsverfolgung, sofortigen Erklärungen und Punkteberechnung.
Anleitung
Dieses Quiz testet dein Verständnis der Code Coverage-Konzepte aus den Vorlesungen 6 und 7.
Vorbereitung: Zuerst lesen
Bevor du dieses Quiz versuchst, studiere die folgenden Vorlesungsabschnitte:
Aus Kapitel 03 (TDD und CI): TDD und CI:
- Teil 2: Das Coverage-Problem - Warum wir Coverage brauchen
- Teil 3: Coverage Reporting einrichten - pytest-cov Grundlagen
- Teil 4: Coverage in CI integrieren - Schwellenwerte und Automatisierung
Aus Kapitel 03 (Testtheorie und Coverage): Testtheorie & Coverage:
- Abschnitt 3: Die Testtheorie - Programm, Modell, Testsuite, Coverage-Kriterium
- Abschnitt 4: Code Coverage (C0 und C1) - Statement vs Branch Coverage, Subsumption
Fokusgebiete: Definitionen, Subsumption-Hierarchie, Coverage-Grenzen
Warum dieses Quiz machen?
Für dein Lernen:
- Selbsteinschätzung: Identifiziere Bereiche, in denen du ein gutes Verständnis hast, und Themen, die Wiederholung brauchen
- Prüfungsvorbereitung: Das Quizformat und die Schwierigkeit spiegeln wider, was du in der Abschlussprüfung erwarten kannst
- Sofortiges Feedback: Erhalte sofort Rückmeldung zu deinen Antworten, um das korrekte Verständnis zu festigen
- Aktives Erinnern: Dich selbst zu testen ist eine der effektivsten Lerntechniken
Richtlinien:
- Jede Frage hat 4 Optionen mit genau einer richtigen Antwort
- Versuche zuerst zu antworten, ohne auf die Vorlesungsmaterialien zu schauen
- Konzentriere dich auf das Verstehen von Konzepten, nicht auf das Auswendiglernen von Definitionen
- Klicke auf “Antwort anzeigen”, um die Erklärung nach jedem Versuch zu sehen
Abschnitt A: Definitionen und Grundlagen
Frage 1: Was ist Statement Coverage?
Statement Coverage (C0) misst:
A) Den Prozentsatz der Funktionen, die mindestens einen Test haben
B) Den Prozentsatz der ausführbaren Code-Anweisungen, die von Tests ausgeführt werden
C) Die Anzahl der Testfälle geteilt durch Codezeilen
D) Den Prozentsatz der Branches, die True zurückgeben
Antwort anzeigen
Richtige Antwort: B
Statement Coverage (C0) misst den Prozentsatz der ausführbaren Anweisungen in deinem Code, die während des Testens ausgeführt werden.
Formel:
\(\text{C0} = \frac{\text{Ausgeführte Anweisungen}}{\text{Gesamte Anweisungen}} \times 100\%\)
Beispiel:
def example(x):
if x > 0: # Statement 1
print("pos") # Statement 2
return x # Statement 3
Wenn du mit example(5) testest:
- Ausgeführte Anweisungen: 1, 2, 3 (alle 3)
- C0 = 3/3 = 100%
Wenn du mit example(-5) testest:
- Ausgeführte Anweisungen: 1, 3 (Anweisung 2 übersprungen)
- C0 = 2/3 = 67%
Warum andere Optionen falsch sind:
- A: Das wäre “Function Coverage”, nicht Statement Coverage
- C: Coverage geht um Ausführung, nicht ein einfaches Verhältnis von Tests zu Code
- D: Das beschreibt Branch-Ergebnisse, nicht Anweisungsausführung
Frage 2: Was ist Branch Coverage?
Branch Coverage (C1) misst:
A) Den Prozentsatz der Code-Branches (True/False-Ergebnisse), die von Tests ausgeführt werden
B) Die Anzahl der if-Anweisungen im Code
C) Den Prozentsatz der Funktionen mit return-Anweisungen
D) Wie oft jede Codezeile ausgeführt wird
Antwort anzeigen
Richtige Antwort: A
Branch Coverage (C1) misst den Prozentsatz der Entscheidungsergebnisse (True/False-Branches), die während des Testens ausgeführt werden.
Formel:
\(\text{C1} = \frac{\text{Ausgeführte Branches}}{\text{Gesamte Branches}} \times 100\%\)
Beispiel:
def example(x):
if x > 0: # Decision with 2 branches: True, False
return "pos" # True branch
return "neg" # False branch (implicit else)
Wenn du mit example(5) testest:
- True Branch: Ausgeführt
- False Branch: NICHT ausgeführt
- C1 = 1/2 = 50%
Um 100% C1 zu erreichen, brauchst du BEIDES:
example(5)→ True Branchexample(-5)→ False Branch
Warum andere Optionen falsch sind:
- B: If-Anweisungen zählen ist keine Coverage; es ist eine Code-Metrik
- C: Das ist kein Standard-Coverage-Maß
- D: Das beschreibt “Execution Count”, nicht Branch Coverage
Frage 3: Was ist ein Coverage-Kriterium?
Ein Coverage-Kriterium ist:
A) Ein Werkzeug, das misst, wie schnell Tests laufen
B) Eine Regel, die definiert, was getestet werden muss, um eine Testsuite als angemessen zu betrachten
C) Die minimale Anzahl von Tests, die pro Funktion erforderlich sind
D) Ein Bewertungsschema für die Benotung von Studenten-Code
Antwort anzeigen
Richtige Antwort: B
Ein Coverage-Kriterium ist eine formale Regel, die definiert, welche Elemente eines Programms von einer Testsuite ausgeführt (abgedeckt) werden müssen, damit diese Suite nach diesem Kriterium als “angemessen” gilt.
Beispiele für Coverage-Kriterien:
- Statement Coverage (C0): Jede Anweisung muss mindestens einmal ausgeführt werden
- Branch Coverage (C1): Jedes Branch-Ergebnis muss mindestens einmal ausgeführt werden
- Path Coverage: Jeder mögliche Pfad durch den Code muss ausgeführt werden
- Condition Coverage: Jeder boolesche Teilausdruck muss True und False sein
Warum das wichtig ist:
Verschiedene Kriterien haben unterschiedliche Stärken (Fehlererkennung) und Kosten (Anzahl benötigter Tests). Die Wahl des richtigen Kriteriums hängt ab von:
- Risikoniveau des Codes
- Verfügbare Test-Ressourcen
- Erforderliches Vertrauensniveau
Warum andere Optionen falsch sind:
- A: Das ist eine Performance-Metrik, kein Coverage-Kriterium
- C: Coverage geht darum, was getestet wird, nicht um eine feste Anzahl
- D: Coverage-Kriterien sind für Test-Angemessenheit, nicht für Benotung
Frage 4: Was bedeutet “Subsumption” beim Testen?
Wenn wir sagen “C1 subsumiert C0,” meinen wir:
A) C1-Tests laufen schneller als C0-Tests
B) Das Erreichen von 100% C1 garantiert automatisch 100% C0
C) C0 ist gründlicher als C1
D) C1 erfordert weniger Tests als C0
Antwort anzeigen
Richtige Antwort: B
Subsumption bedeutet, dass ein Coverage-Kriterium “stärker” ist als ein anderes. Wenn Kriterium A Kriterium B subsumiert, dann garantiert das Erreichen von 100% Coverage unter A automatisch 100% Coverage unter B.
C1 subsumiert C0, weil:
Um beide Branches einer Entscheidung abzudecken:
if condition:
statement_A # True branch
else:
statement_B # False branch
- Du MUSST
statement_Aausführen (um True Branch abzudecken) - Du MUSST
statement_Bausführen (um False Branch abzudecken) - Daher sind beide Anweisungen abgedeckt
Das Umgekehrte gilt NICHT:
Du kannst 100% C0 ohne 100% C1 haben:
def risky(x, logging=True):
if logging:
log_event(x) # Always executed if logging=True
return process(x) # Always executed
Nur mit logging=True testen ergibt:
- C0: 100% (alle Anweisungen ausgeführt)
- C1: 50% (nur True Branch von
if logginggetestet)
Warum andere Optionen falsch sind:
- A: Subsumption geht um Coverage, nicht Geschwindigkeit
- C: Das Gegenteil ist wahr; C1 ist gründlicher
- D: C1 erfordert typischerweise MEHR Tests (muss beide Branches abdecken)
Frage 5: Was ist Test-Angemessenheit?
Eine Testsuite ist “angemessen” nach einem Coverage-Kriterium, wenn:
A) Alle Tests ohne Fehler bestehen
B) Die Testsuite 100% Coverage nach diesem Kriterium erreicht
C) Mindestens 10 Tests für jedes Modul existieren
D) Tests in unter 1 Sekunde laufen
Antwort anzeigen
Richtige Antwort: B
Test-Angemessenheit ist eine Eigenschaft einer Testsuite relativ zu einem spezifischen Coverage-Kriterium. Eine Testsuite ist angemessen, wenn sie 100% Coverage nach dem gewählten Kriterium erreicht.
Beispiele:
- C0-angemessen: Jede Anweisung wird von mindestens einem Test ausgeführt
- C1-angemessen: Jedes Branch-Ergebnis wird von mindestens einem Test ausgeführt
Wichtige Unterscheidung:
- Angemessen bedeutet “erfüllt die Anforderungen des Kriteriums”
- Angemessen bedeutet NICHT “Tests sind korrekt” oder “Code ist fehlerfrei”
Eine Testsuite kann C0-angemessen sein und trotzdem:
- Schwache Assertions haben
- Grenzfälle verpassen
- Tatsächliche Bugs nicht erkennen
Warum andere Optionen falsch sind:
- A: Tests bestehen geht um Korrektheit, nicht Angemessenheit
- C: Angemessenheit geht nicht um Anzahl; es geht um Coverage
- D: Geschwindigkeit ist ein Performance-Thema, nicht Angemessenheit
Abschnitt B: Verständnis und Berechnung
Frage 6: Warum subsumiert C1 C0?
Betrachte diesen Code:
def check(value):
if value > 0:
return "positive"
else:
return "negative"
Warum gibt das Erreichen von 100% C1 automatisch 100% C0?
A) Weil C1 mehr Anweisungen zählt als C0
B) Weil das Abdecken beider Branches erfordert, alle Anweisungen in diesen Branches auszuführen
C) Weil C1-Tests länger sind als C0-Tests
D) Weil Branch Coverage nach Statement Coverage berechnet wird
Antwort anzeigen
Richtige Antwort: B
Um 100% C1 bei diesem Code zu erreichen, musst du abdecken:
- True Branch (
value > 0ist True) → Führtreturn "positive"aus - False Branch (
value > 0ist False) → Führtreturn "negative"aus
Durch das Abdecken beider Branches hast du notwendigerweise ausgeführt:
- Zeile 2:
if value > 0:(die Bedingung selbst) - Zeile 3:
return "positive"(True Branch Anweisung) - Zeile 5:
return "negative"(False Branch Anweisung)
Alle 3 ausführbaren Anweisungen sind abgedeckt, also C0 = 100%.
Visueller Beweis:
| Test | Abgedeckter Branch | Ausgeführte Anweisungen |
|---|---|---|
check(5) |
True | Zeilen 2, 3 |
check(-5) |
False | Zeilen 2, 5 |
| Kombiniert | 100% C1 | Alle Anweisungen (100% C0) |
Warum andere Optionen falsch sind:
- A: C1 “zählt” keine Anweisungen; es zählt Branch-Ergebnisse
- C: Testlänge hat nichts mit Subsumption zu tun
- D: Die Reihenfolge der Berechnung beeinflusst die Beziehung nicht
Frage 7: Statement Coverage berechnen
Gegeben dieser Code und Test:
def categorize(score):
if score >= 90:
return "A"
elif score >= 80:
return "B"
elif score >= 70:
return "C"
else:
return "F"
# Test:
def test_high_score():
assert categorize(95) == "A"
Was ist die Statement Coverage (C0)?
A) 25%
B) 40%
C) 50%
D) 100%
Selbst ausprobieren
Du kannst deine Antwort mit pytest-cov überprüfen! Erstelle diese Dateien:
src/grading.py:
def categorize(score):
if score >= 90:
return "A"
elif score >= 80:
return "B"
elif score >= 70:
return "C"
else:
return "F"
tests/test_grading.py:
from src.grading import categorize
def test_high_score():
assert categorize(95) == "A"
Coverage ausführen:
uv run pytest tests/ --cov=src --cov-report=term-missing
Voraussetzungen: Python 3.12+, uv Package Manager, pytest und pytest-cov installiert (uv add pytest pytest-cov).
Antwort anzeigen
Richtige Antwort: B
Zählen wir die ausführbaren Anweisungen:
def categorize(score):
if score >= 90: # Statement 1 (condition check)
return "A" # Statement 2 (executed)
elif score >= 80: # Statement 3 (NOT executed - short-circuit)
return "B" # Statement 4 (NOT executed)
elif score >= 70: # Statement 5 (NOT executed)
return "C" # Statement 6 (NOT executed)
else:
return "F" # Statement 7 (NOT executed)
Mit categorize(95):
- Anweisung 1: Ausgeführt (Bedingung ist True)
- Anweisung 2: Ausgeführt (gibt “A” zurück)
- Anweisungen 3-7: NICHT ausgeführt (Funktion kehrt früh zurück)
Berechnung:
- Ausgeführt: 2 Anweisungen
- Gesamt: 5 Anweisungen (die 4 returns + der Funktionseintritt/erste Bedingung)
- C0 = 2/5 = 40%
Hinweis: Genaue Zahlen können je nachdem variieren, wie dein Coverage-Tool Anweisungen zählt (manche zählen if und elif separat, manche nicht). Die Kernaussage ist, dass nur ein kleiner Teil des Codes ausgeführt wird.
Warum andere Optionen falsch sind:
- A (25%): Zu niedrig; wir haben mehr als 1 von 4 ausgeführt
- C (50%): Wir haben nicht die Hälfte der Anweisungen ausgeführt
- D (100%): Wir haben eindeutig die B, C und F Branches übersprungen
Frage 8: Branch Coverage berechnen
Mit dem gleichen Code aus Frage 7:
def categorize(score):
if score >= 90:
return "A"
elif score >= 80:
return "B"
elif score >= 70:
return "C"
else:
return "F"
# Tests:
def test_scores():
assert categorize(95) == "A"
assert categorize(85) == "B"
Was ist die Branch Coverage (C1)?
A) 25%
B) 50%
C) 75%
D) 100%
Selbst ausprobieren
Überprüfe deine Antwort mit pytest-cov mit aktivierter Branch Coverage:
tests/test_grading.py:
from src.grading import categorize
def test_scores():
assert categorize(95) == "A"
assert categorize(85) == "B"
Mit Branch Coverage ausführen:
uv run pytest tests/ --cov=src --cov-branch --cov-report=term-missing
Das --cov-branch Flag aktiviert Branch Coverage Reporting. Achte auf die “Branch” und “BrPart” Spalten in der Ausgabe.
Antwort anzeigen
Richtige Antwort: B
Identifizieren wir alle Branches:
if score >= 90: # Branch 1 (True), Branch 2 (False)
return "A"
elif score >= 80: # Branch 3 (True), Branch 4 (False)
return "B"
elif score >= 70: # Branch 5 (True), Branch 6 (False)
return "C"
else:
return "F"
Gesamte Branches: 6 (jede Bedingung hat True und False Ergebnisse)
Mit unseren Tests:
| Test | score >= 90 | score >= 80 | score >= 70 |
|---|---|---|---|
categorize(95) |
True (Branch 1) | - | - |
categorize(85) |
False (Branch 2) | True (Branch 3) | - |
Abgedeckte Branches: 1, 2, 3 = 3 Branches NICHT abgedeckte Branches: 4, 5, 6 (brauchen Scores wie 75 und 50)
Berechnung:
C1 = 3/6 = 50%
Um 100% C1 zu erreichen, bräuchten wir:
categorize(95)→ A (deckt score >= 90 True ab)categorize(85)→ B (deckt score >= 90 False, score >= 80 True ab)categorize(75)→ C (deckt score >= 80 False, score >= 70 True ab)categorize(50)→ F (deckt score >= 70 False ab)
Frage 9: Coverage Report interpretieren
Gegeben diese pytest-cov Ausgabe:
Name Stmts Miss Cover Missing
-----------------------------------------------
src/utils.py 24 6 75% 18-20, 35-37
-----------------------------------------------
Was sagt dir “Missing: 18-20, 35-37”?
A) Zeilen 18-20 und 35-37 haben Syntaxfehler
B) Zeilen 18-20 und 35-37 wurden von keinem Test ausgeführt
C) Zeilen 18-20 und 35-37 sollten gelöscht werden
D) Zeilen 18-20 und 35-37 enthalten den wichtigsten Code
Antwort anzeigen
Richtige Antwort: B
Die “Missing” Spalte zeigt Zeilennummern, die von keinem Test in deiner Testsuite nicht ausgeführt wurden.
Was das bedeutet:
- Zeilen 18, 19, 20: Nicht von Tests abgedeckt
- Zeilen 35, 36, 37: Nicht von Tests abgedeckt
- Diese Zeilen repräsentieren ungetestete Codepfade
Was dagegen zu tun ist:
- Schau dir den Code an diesen Zeilen an
- Verstehe warum sie nicht ausgeführt wurden:
- Ist es ein bedingter Branch, der nicht ausgelöst wurde?
- Ist es Fehlerbehandlungscode?
- Ist es toter Code, der entfernt werden sollte?
- Füge Tests hinzu, die diese Zeilen ausführen (wenn es gültiger Code ist)
Beispielszenario:
# Line 18-20 might be:
if special_case:
handle_special()
return early_result
# Your tests never triggered special_case = True
Warum andere Optionen falsch sind:
- A: Coverage-Tools erkennen keine Syntaxfehler; sie messen Ausführung
- C: Fehlende Zeilen sind nicht unbedingt schlecht; sie brauchen nur Tests
- D: Die Wichtigkeit von Code hängt nicht davon ab, ob er getestet ist
Frage 10: Report mit Code abgleichen
Gegeben dieser Code:
1
2
3
4
5
6
7
8
def validate(value):
if value is None:
return "Error"
if value < 0:
return "Negative"
if value == 0:
return "Zero"
return "Positive"
Und dieser Coverage Report:
Missing: 3, 7
Welche Bedingungen waren NIEMALS False während des Testens?
A) value is None und value < 0
B) value is None und value == 0
C) value < 0 und value == 0
D) Alle Bedingungen wurden als True und False getestet
Antwort anzeigen
Richtige Antwort: B
Analysieren wir, was “Missing: 3, 7” bedeutet:
- Zeile 3 fehlt:
return "Error"wurde nie ausgeführt- Das bedeutet
value is Nonewar nie True - Also war
value is Noneimmer False
- Das bedeutet
- Zeile 7 fehlt:
return "Zero"wurde nie ausgeführt- Das bedeutet
value == 0war nie True - Also war
value == 0immer False
- Das bedeutet
Was wahrscheinlich getestet wurde:
validate(5)→ Positive (Zeilen 2, 4, 6, 8 ausgeführt)validate(-3)→ Negative (Zeilen 2, 4, 5 ausgeführt)
Was NICHT getestet wurde:
validate(None)→ Würde Zeile 3 ausführenvalidate(0)→ Würde Zeile 7 ausführen
Warum andere Optionen falsch sind:
- A: Zeile 5 (
return "Negative") fehlt NICHT, also warvalue < 0irgendwann True - C: Zeile 5 fehlt NICHT, also wurde
value < 0abgedeckt - D: Zeilen 3 und 7 fehlen, also wurden nicht alle Bedingungen vollständig getestet
Abschnitt C: Grenzen von Coverage
Frage 11: Warum garantiert 100% Coverage keinen fehlerfreien Code?
Eine Testsuite erreicht 100% Statement Coverage. Das bedeutet:
A) Der Code ist definitiv korrekt und produktionsreif
B) Alle möglichen Eingaben wurden getestet
C) Jede Anweisung wurde ausgeführt, aber die Tests könnten schwache oder fehlende Assertions haben
D) Der Code hat keine Bugs
Antwort anzeigen
Richtige Antwort: C
100% Coverage bedeutet, dass jede Anweisung ausgeführt wurde, aber sagt nichts darüber aus:
- Ob Assertions korrektes Verhalten verifizieren
- Ob alle wichtigen Eingaben getestet wurden
- Ob die Logik tatsächlich korrekt ist
Beispiel für 100% Coverage ohne Bug-Erkennung:
def divide(a, b):
return a / b # Bug: doesn't handle b=0
def test_divide():
result = divide(10, 2)
assert result is not None # Weak assertion! Doesn't check value
Dies erreicht 100% Coverage, aber:
- Verifiziert nicht das tatsächliche Ergebnis (sollte 5 sein)
- Testet b=0 nicht (was abstürzen würde)
- Gibt falsches Vertrauen
Was Coverage dir SAGT:
- Welcher Code überhaupt nicht ausgeführt wurde (definitiv nicht getestet)
- Lücken in deiner Testsuite
Was Coverage dir NICHT sagt:
- Ob Tests tatsächlich Korrektheit verifizieren
- Ob Grenzfälle behandelt werden
- Ob der Code fehlerfrei ist
Warum andere Optionen falsch sind:
- A: Korrektheit erfordert richtige Assertions, nicht nur Ausführung
- B: Du kannst 100% Coverage mit sehr wenigen Eingaben erreichen
- D: Coverage misst Ausführung, nicht Korrektheit
Frage 12: Schlechter Test mit hoher Coverage
Betrachte diesen Code und Test:
def calculate_total(items, discount_percent):
total = sum(items)
if discount_percent > 0:
discount = total * (discount_percent / 100)
total = total - discount
return total
def test_calculate_total():
result = calculate_total([10, 20, 30], 10)
# What's wrong with this test?
assert result > 0
Was ist das Problem mit diesem Test?
A) Er erreicht keine 100% Coverage
B) Er verwendet die falsche Assertion-Syntax
C) Die Assertion ist zu schwach - sie verifiziert nicht den korrekten Wert
D) Der Testname ist nicht aussagekräftig genug
Antwort anzeigen
Richtige Antwort: C
Der Test erreicht hohe Coverage durch Ausführen von:
total = sum(items)→ 60if discount_percent > 0:→ Truediscount = total * (discount_percent / 100)→ 6total = total - discount→ 54return total→ gibt 54 zurück
Aber die Assertion assert result > 0 ist extrem schwach:
- Sie würde für
result = 1bestehen (falsch!) - Sie würde für
result = 100000bestehen (falsch!) - Sie würde für
result = 54bestehen (korrekt)
Ein richtiger Test sollte den exakt erwarteten Wert verifizieren:
def test_calculate_total_with_discount():
# Arrange
items = [10, 20, 30]
discount_percent = 10
# Act
result = calculate_total(items, discount_percent)
# Assert - verify the exact expected value
expected = 54 # (10+20+30) - 10% = 60 - 6 = 54
assert result == expected
Kernaussage:
Coverage sagt dir, dass der Code gelaufen ist, aber nur gute Assertions sagen dir, dass der Code korrekt funktioniert hat.
Warum andere Optionen falsch sind:
- A: Der Test erreicht wahrscheinlich gute Coverage (alle Zeilen ausgeführt)
- B: Die Syntax ist gültiges Python
- D: Obwohl wahr, ist die schwache Assertion das kritische Problem
Frage 13: Was kann Coverage NICHT erkennen?
Welchen der folgenden Bugs würde 100% Statement Coverage wahrscheinlich VERPASSEN?
A) Eine fehlende Import-Anweisung
B) Eine falsche Berechnungsformel
C) Einen Syntaxfehler im Code
D) Eine undefinierte Variable
Antwort anzeigen
Richtige Antwort: B
Eine falsche Berechnungsformel könnte trotzdem perfekt ausgeführt werden und 100% Coverage erreichen, während sie falsche Ergebnisse produziert.
Beispiel:
def calculate_area(radius):
return 3.14 * radius * radius # Bug: should use math.pi
def test_area():
result = calculate_area(10)
assert result is not None # Passes! But result is slightly wrong
Dies erreicht 100% Coverage, aber:
- Die Formel verwendet
3.14stattmath.pi - Das Ergebnis ist ungefähr 314 statt 314.159…
- Eine schwache Assertion fängt das nicht ab
Was Coverage (indirekt) erkennen KANN:
- A (fehlender Import): Würde abstürzen, wenn dieses Modul verwendet wird
- C (Syntaxfehler): Python läuft gar nicht
- D (undefinierte Variable): Würde NameError auslösen, wenn ausgeführt
Kernaussage:
Coverage erkennt Abstürze (Code, der nicht läuft), aber nicht logische Fehler (Code, der läuft, aber falsche Ausgaben produziert).
Deshalb brauchst du:
- Gute Coverage (Code wird ausgeführt)
- Starke Assertions (Verhalten wird verifiziert)
- Grenzfall-Tests (Randbedingungen werden geprüft)
Frage 14: Wann ist 70% Coverage akzeptabel?
In welcher Situation könnte 70% Coverage völlig akzeptabel sein?
A) Niemals - strebe immer 100% an
B) Wenn die nicht abgedeckten 30% toter Code oder unerreichbare Fehlerbehandler sind
C) Wenn Tests zu teuer zu schreiben sind
D) Wenn das Projekt fast fertig ist
Antwort anzeigen
Richtige Antwort: B
70% Coverage kann akzeptabel sein, wenn der nicht abgedeckte Code ist:
- Toter Code, der entfernt werden sollte
- Defensive Fehlerbehandler für “unmögliche” Bedingungen
- Plattformspezifischer Code, der für die aktuelle Umgebung nicht relevant ist
- Debug/Logging-Code, der nicht kritisch zu testen ist
Beispiele für akzeptablen nicht abgedeckten Code:
def process_file(path):
try:
with open(path) as f:
return f.read()
except PermissionError:
# Only happens in restricted environments
log_error("Permission denied")
return None
except Exception as e:
# Defensive catch-all, should "never" happen
log_critical(f"Unexpected: {e}")
raise
Die except-Blöcke könnten nicht abgedeckt sein, wenn deine Tests nur zugängliche Dateien verwenden. Das ist oft OK.
Wann 70% NICHT akzeptabel ist:
- Kern-Geschäftslogik ist nicht abgedeckt
- Happy-Path-Funktionalität ist nicht getestet
- Die 30% enthalten kritischen Code
Warum andere Optionen falsch sind:
- A: 100% ist nicht immer praktikabel oder sinnvoll
- C: Kosten allein sind kein guter Grund; priorisiere kritischen Code
- D: Projektphase bestimmt nicht akzeptable Coverage
Frage 15: Coverage vs Korrektheit
Welche Aussage beschreibt am besten die Beziehung zwischen Coverage und Korrektheit?
A) Hohe Coverage beweist Code-Korrektheit
B) Coverage misst Ausführung; Korrektheit erfordert Assertions und Testdesign
C) Niedrige Coverage beweist, dass Code inkorrekt ist
D) Coverage und Korrektheit sind dasselbe
Antwort anzeigen
Richtige Antwort: B
Coverage und Korrektheit sind verschiedene Konzepte:
| Aspekt | Coverage | Korrektheit |
|---|---|---|
| Misst | Code-Ausführung | Verhaltensverifikation |
| Werkzeug | pytest-cov | Assertions |
| Frage | “Wurde dieser Code ausgeführt?” | “Funktioniert dieser Code richtig?” |
| Automatisierbar | Ja (vollständig) | Teilweise (Assertions brauchen menschliches Design) |
Hohe Coverage + schwache Assertions = Falsches Vertrauen
def test_broken():
result = buggy_function() # Runs the code (coverage!)
assert result is not None # Doesn't check correctness
Niedrige Coverage + starke Assertions = Unvollständiges Testen
def test_one_path():
assert calculate(10) == 100 # Correct for this input
# But what about calculate(-5)? calculate(0)?
Ideal: Hohe Coverage + starke Assertions + gutes Testdesign
def test_positive():
assert calculate(10) == 100
def test_negative():
assert calculate(-5) == -50
def test_zero():
assert calculate(0) == 0
def test_edge_case():
assert calculate(0.001) == 0.01
Warum andere Optionen falsch sind:
- A: Coverage beweist Ausführung, nicht Korrektheit
- C: Niedrige Coverage bedeutet ungetesteten Code, nicht inkorrekten Code
- D: Sie sind komplementäre, aber unterschiedliche Konzepte
Abschnitt D: Anwendung
Frage 16: Welcher Test würde Zeile X abdecken?
Gegeben dieser Code:
1
2
3
4
5
6
7
8
9
def classify_age(age):
if age < 0:
return "Invalid"
elif age < 18:
return "Minor"
elif age < 65:
return "Adult"
else:
return "Senior"
Dein Coverage Report zeigt, dass Zeile 9 fehlt. Welcher Test würde sie abdecken?
A) assert classify_age(-5) == "Invalid"
B) assert classify_age(17) == "Minor"
C) assert classify_age(30) == "Adult"
D) assert classify_age(70) == "Senior"
Selbst ausprobieren
Teste deine Hypothese! Erstelle diese Dateien und führe Coverage aus:
src/age_classifier.py:
def classify_age(age):
if age < 0:
return "Invalid"
elif age < 18:
return "Minor"
elif age < 65:
return "Adult"
else:
return "Senior"
tests/test_age.py (initiale Tests, die Zeile 9 nicht abdecken):
from src.age_classifier import classify_age
def test_ages():
assert classify_age(-5) == "Invalid"
assert classify_age(17) == "Minor"
assert classify_age(30) == "Adult"
Coverage ausführen, um Zeile 9 als fehlend zu sehen:
uv run pytest tests/ --cov=src --cov-report=term-missing
Dann füge deinen gewählten Test hinzu und führe erneut aus, um zu verifizieren, dass er Zeile 9 abdeckt!
Antwort anzeigen
Richtige Antwort: D
Zeile 9 (return "Senior") ist im else-Branch, der ausgeführt wird, wenn:
age < 0ist False (age ist 0 oder positiv)age < 18ist False (age ist 18 oder älter)age < 65ist False (age ist 65 oder älter)
Also brauchen wir age >= 65, um Zeile 9 zu erreichen.
Analyse jeder Option:
| Test | age | Zeile 3? | Zeile 5? | Zeile 7? | Zeile 9? |
|---|---|---|---|---|---|
| A: -5 | -5 | Ja | Nein | Nein | Nein |
| B: 17 | 17 | Nein | Ja | Nein | Nein |
| C: 30 | 30 | Nein | Nein | Ja | Nein |
| D: 70 | 70 | Nein | Nein | Nein | Ja |
Nur Option D deckt Zeile 9 ab.
Warum andere Optionen falsch sind:
- A: Deckt Zeile 3 ab (age < 0 ist True)
- B: Deckt Zeile 5 ab (age < 18 ist True)
- C: Deckt Zeile 7 ab (age < 65 ist True)
Frage 17: Test für nicht abgedeckten Branch entwerfen
Gegeben dieser Code und Coverage Report:
1
2
3
4
5
6
7
8
9
def calculate_shipping(weight, express=False):
if weight <= 0:
raise ValueError("Invalid")
base_cost = weight * 2.0
if express:
return base_cost * 1.5
return base_cost
Coverage Report: Fehlende Zeilen 3, 8
Um BEIDE fehlenden Zeilen abzudecken, brauchst du Tests mit welchen Eingaben?
A) weight=5, express=True nur
B) weight=-1 und weight=5, express=True
C) weight=0 und weight=5, express=False
D) weight=5, express=True und weight=10, express=True
Antwort anzeigen
Richtige Antwort: B
Verfolgen wir, was jede fehlende Zeile abdeckt:
Zeile 3 (raise ValueError): Braucht weight <= 0
weight = -1→ Zeile 2 ist True → Zeile 3 wird ausgeführt
Zeile 8 (return base_cost * 1.5): Braucht weight > 0 UND express=True
weight = 5, express = True→ Erreicht Zeile 7, Zeile 7 ist True → Zeile 8 wird ausgeführt
Analyse der Optionen:
| Option | Deckt Zeile 3 ab? | Deckt Zeile 8 ab? |
|---|---|---|
| A | Nein (weight > 0) | Ja |
| B | Ja (weight = -1) | Ja (weight=5, express=True) |
| C | Ja (weight = 0) | Nein (express=False) |
| D | Nein | Ja (beide sind express=True) |
Nur Option B deckt BEIDE fehlenden Zeilen ab.
Warum andere Optionen falsch sind:
- A: Deckt nur Zeile 8 ab, nicht Zeile 3
- C: Deckt nur Zeile 3 ab, nicht Zeile 8
- D: Beide Tests haben positives weight, decken nie Zeile 3 ab
Frage 18: pytest-cov Ausgabe interpretieren
Gegeben diese pytest-cov Ausgabe mit aktivierter Branch Coverage:
Name Stmts Miss Branch BrPart Cover
----------------------------------------------------
src/validator.py 20 2 10 3 82%
----------------------------------------------------
Was bedeutet “BrPart: 3”?
A) 3 Branches wurden getestet
B) 3 Branches sind teilweise abgedeckt (nur ein Ergebnis getestet)
C) 3 Branches sollten gelöscht werden
D) 3 Branches haben Fehler
Antwort anzeigen
Richtige Antwort: B
BrPart steht für “Branch Partial” - es zählt Branches, bei denen nur ein Ergebnis (True ODER False) getestet wurde, aber nicht beide.
Beispiel für teilweise Branch Coverage:
def process(value, debug=True):
if debug: # Branch: tested only when debug=True
log(value)
return transform(value)
Wenn alle Tests debug=True verwenden:
- True Branch: Abgedeckt (log wird aufgerufen)
- False Branch: NICHT abgedeckt (log wird übersprungen)
- Dieser Branch ist “teilweise abgedeckt” → trägt zu BrPart bei
Spaltenbedeutungen:
- Stmts: Gesamte Anweisungen (20)
- Miss: Nicht ausgeführte Anweisungen (2)
- Branch: Gesamte Branch-Ergebnisse (10)
- BrPart: Teilweise abgedeckte Branches (3)
- Cover: Gesamter Coverage-Prozentsatz (82%)
Um teilweise Branches zu beheben:
Füge Tests hinzu, die das gegenteilige Bedingungsergebnis auslösen.
Warum andere Optionen falsch sind:
- A: BrPart zählt unvollständige Branches, nicht vollständige
- C: Teilweise Coverage ist kein Grund, Code zu löschen
- D: Dies ist eine Coverage-Metrik, kein Fehlerbericht
Frage 19: Angemessenen Coverage-Schwellenwert wählen
Dein Team richtet eine CI-Pipeline für ein Finanztransaktionssystem ein. Welcher Coverage-Schwellenwert ist am angemessensten?
A) 50% - Niedriger Schwellenwert, um Deployments nicht zu blockieren
B) 70% - Industriestandard
C) 85-90% - Hoher Schwellenwert mit Fokus auf kritische Pfade
D) 100% - Finanzsysteme brauchen vollständige Coverage
Antwort anzeigen
Richtige Antwort: C
Für ein Finanztransaktionssystem ist hohe Coverage wichtig, weil:
- Bugs können finanzielle Verluste verursachen
- Grenzfälle bei Geldhandhabung sind kritisch
- Regulatorische Anforderungen können Tests vorschreiben
Warum 85-90% statt 100%:
- 100% ist oft unpraktisch (Fehlerbehandler, Debug-Code)
- 100% kann dazu führen, Implementierungsdetails zu testen
- Fokus sollte auf sinnvoller Coverage kritischer Pfade liegen
Best Practice für kritische Systeme:
- Basis-Schwellenwert setzen (85-90%)
- 100% bei kritischen Modulen erfordern (z.B.
transactions.py) - Niedrigere Coverage bei nicht-kritischen Hilfsfunktionen erlauben
- Starke Assertions durchsetzen, nicht nur Ausführung
Beispiel CI-Konfiguration:
- name: Run tests with coverage
run: pytest --cov=src --cov-fail-under=85 --cov-report=term-missing
Warum andere Optionen falsch sind:
- A (50%): Zu niedrig für Finanzsysteme
- B (70%): Industriestandard für allgemeine Apps, aber Finanzen braucht mehr
- D (100%): Unpraktisch und kann dazu führen, die Metrik zu manipulieren
Frage 20: Zweck der CI-Integration
Warum Coverage-Checks in eine CI/CD-Pipeline integrieren?
A) Um Bugs im Code automatisch zu beheben
B) Um zu verhindern, dass Code gemerged wird, der die Test-Coverage unter den Schwellenwert senkt
C) Um Tests schneller zu machen
D) Um manuelles Code-Review zu ersetzen
Antwort anzeigen
Richtige Antwort: B
CI Coverage-Integration dient als Qualitäts-Gate, das:
- Coverage bei jedem Pull Request misst
- Den Build fehlschlagen lässt, wenn Coverage unter den Schwellenwert fällt
- Verhindert, dass sich “Coverage-Schulden” ansammeln
- Coverage für das Team sichtbar macht
Wie es funktioniert:
# In GitHub Actions workflow
- name: Run tests with coverage
run: uv run pytest --cov=src --cov-fail-under=80
# If coverage < 80%, the workflow fails
# PR cannot be merged until coverage is restored
Vorteile:
- Automatische Durchsetzung: Keine manuelle Überprüfung nötig
- Frühes Feedback: Entwickler wissen sofort, ob sie mehr Tests brauchen
- Trend-Schutz: Coverage kann nicht stillschweigend abnehmen
- Team-Sichtbarkeit: Jeder sieht Coverage in PR-Checks
Was CI Coverage NICHT macht:
- Bugs automatisch beheben
- Tests beschleunigen
- Code-Review ersetzen (Menschen werden noch gebraucht)
Warum andere Optionen falsch sind:
- A: CI misst; es behebt nicht
- C: Coverage-Checks fügen Zeit hinzu, reduzieren sie nicht
- D: Code-Review fängt Design-Probleme, die Coverage nicht kann
Bewertungsleitfaden
- 18-20 richtig: Ausgezeichnet! Du hast ein starkes Verständnis von Coverage-Konzepten und ihrer praktischen Anwendung
- 15-17 richtig: Gutes Verständnis! Wiederhole die Bereiche, die du verpasst hast, um dein Wissen zu festigen
- 12-14 richtig: Ausreichendes Verständnis. Schau dir die Vorlesungsmaterialien zu C0/C1 und Coverage-Grenzen noch einmal an
- 9-11 richtig: Wiederholung nötig. Konzentriere dich auf die Grundlagen in Abschnitten A und B
- Unter 9: Bitte wiederhole Vorlesungen 6 und 7 sorgfältig, bevor du fortfährst
Wichtige Erkenntnisse zum Merken
- Statement Coverage (C0) misst Prozentsatz der ausgeführten Anweisungen
- Branch Coverage (C1) misst Prozentsatz der ausgeführten Branch-Ergebnisse (True/False)
- C1 subsumiert C0: 100% Branch Coverage garantiert 100% Statement Coverage
- Coverage-Kriterium definiert, was eine Testsuite “angemessen” macht
- 100% Coverage ≠ fehlerfrei: Tests können Code ausführen, ohne ihn zu verifizieren
- Schwache Assertions sind der Feind:
assert x is not Nonefängt nichts ab - Coverage zeigt Lücken: “Missing” Zeilen sagen dir, was NICHT getestet ist
- Teilweise Branches (BrPart) bedeuten, nur ein Ergebnis wurde getestet
- 70-80% ist akzeptabel für die meisten Projekte; kritische Systeme brauchen mehr
- CI-Integration verhindert Coverage-Regression
Was kommt als Nächstes?
Fahre mit der CFG Tracing Übung fort, um zu üben, Coverage visuell mit Kontrollflussgraphen zu analysieren.