03 Grundlagen des Testens: CFG Tracing Übung
December 2025 (4247 Words, 24 Minutes)
Neu: Interaktive Übung verfügbar!
Möchtest du deinen Fortschritt verfolgen und sofortiges Feedback erhalten? Probiere unsere neue interaktive Version mit Fortschrittsverfolgung, sofortigen Erklärungen und Punkteberechnung.
CFG Tracing Übung: Visualisierung von Statement- und Branch-Coverage
Einführung
Diese Übung hilft dir, Code-Coverage zu verstehen, indem du Testfälle durch Kontrollflussgraphen (CFGs) verfolgst. Durch die Visualisierung, welche Knoten (Anweisungen) und Kanten (Branches) abgedeckt werden, entwickelst du ein Gespür dafür, wie Coverage funktioniert.
Vorbereitung: Zuerst lesen
Bevor du diese Übung versuchst, studiere die folgenden Vorlesungsabschnitte:
Aus Kapitel 03 (Testtheorie und Coverage): Testtheorie & Coverage:
- Abschnitt 4: Code Coverage (C0 und C1) - CFG-Beispiele, Knoten/Kanten zählen
- Abschnitt 5: Systematischer Testentwurf für Branch Coverage - Tests aus CFGs ableiten
Fokusgebiete: CFGs lesen, Pfade verfolgen, C0 und C1 berechnen
Lernziele:
- Kontrollflussgraphen lesen und verstehen
- Testfallausführung durch einen CFG verfolgen
- Statement Coverage (C0) aus Knotenabdeckung berechnen
- Branch Coverage (C1) aus Kantenabdeckung berechnen
- Verstehen, warum C1 C0 subsumiert
Anleitung:
- Studiere den Code und seinen zugehörigen CFG
- Verfolge die gegebenen Testfälle durch den Graphen
- Identifiziere, welche Knoten und Kanten abgedeckt werden
- Berechne die Coverage-Prozentsätze
- Klicke auf “Lösung anzeigen”, um deine Antwort zu überprüfen
Zeit: 35-45 Minuten für alle Übungen
Übung 1: Einfacher CFG - classify_number()
Code
def classify_number(x):
if x > 0: # Node 1 (decision)
return "pos" # Node 2
elif x < 0: # Node 3 (decision)
return "neg" # Node 4
else:
return "zero" # Node 5
Kontrollflussgraph
flowchart TD
START([Start]) --> N1{x > 0?}
N1 -->|True| N2[return 'pos']
N1 -->|False| N3{x < 0?}
N3 -->|True| N4[return 'neg']
N3 -->|False| N5[return 'zero']
N2 --> END([End])
N4 --> END
N5 --> END
Testfall
def test_positive():
assert classify_number(5) == "pos"
Fragen
- Welche Knoten werden von diesem Test abgedeckt?
- Welche Kanten werden von diesem Test abgedeckt?
- Was ist die Statement Coverage (C0)?
- Was ist die Branch Coverage (C1)?
Lösung anzeigen
Ablaufverfolgung
Mit Eingabe x = 5:
- START → N1: Führe
if x > 0aus → 5 > 0 ist True - N1 → N2: Nimm True-Kante, führe
return "pos"aus - N2 → END: Funktion gibt zurück
Abgedeckte Knoten
- N1 (Entscheidungsprüfung): Abgedeckt
- N2 (return “pos”): Abgedeckt
- N3 (elif-Prüfung): NICHT abgedeckt (übersprungen)
- N4 (return “neg”): NICHT abgedeckt
- N5 (return “zero”): NICHT abgedeckt
Abgedeckte Kanten
| Kante | Abgedeckt? |
|---|---|
| START → N1 | Ja |
| N1 → N2 (True) | Ja |
| N1 → N3 (False) | Nein |
| N3 → N4 (True) | Nein |
| N3 → N5 (False) | Nein |
| N2 → END | Ja |
| N4 → END | Nein |
| N5 → END | Nein |
Coverage-Berechnung
Statement Coverage (C0):
\(\text{C0} = \frac{\text{Abgedeckte Knoten}}{\text{Gesamte Knoten}} = \frac{2}{5} = 40\%\)
Branch Coverage (C1):
Gesamte Branch-Kanten (Entscheidungsergebnisse): 4 (zwei von N1, zwei von N3)
\(\text{C1} = \frac{\text{Abgedeckte Branches}}{\text{Gesamte Branches}} = \frac{1}{4} = 25\%\)
Wichtige Erkenntnis
Ein einzelner Test deckt nur einen Pfad ab. Um 100% Coverage zu erreichen, brauchen wir Tests für:
x > 0→ Positiver Fallx < 0→ Negativer Fallx == 0→ Null-Fall
Übung 2: Mehrere Testfälle
Code (gleich wie Übung 1)
def classify_number(x):
if x > 0:
return "pos"
elif x < 0:
return "neg"
else:
return "zero"
Testfälle
def test_positive():
assert classify_number(5) == "pos"
def test_negative():
assert classify_number(-3) == "neg"
Fragen
- Verfolge beide Tests durch den CFG
- Was ist die kombinierte C0?
- Was ist die kombinierte C1?
- Welcher Test fehlt noch für 100% Coverage?
Lösung anzeigen
Ablaufverfolgung: test_positive (x = 5)
START → N1 (True) → N2 → END
Abgedeckte Knoten: N1, N2 Abgedeckte Kanten: START→N1, N1→N2 (True), N2→END
Ablaufverfolgung: test_negative (x = -3)
START → N1 (False) → N3 (True) → N4 → END
Abgedeckte Knoten: N1, N3, N4 Abgedeckte Kanten: START→N1, N1→N3 (False), N3→N4 (True), N4→END
Kombinierte Coverage
Abgedeckte Knoten: N1, N2, N3, N4 (4 von 5) NICHT abgedeckte Knoten: N5 (return “zero”)
Statement Coverage (C0):
\(\text{C0} = \frac{4}{5} = 80\%\)
Abgedeckte Kanten (Branches):
- N1 → N2 (True): Abgedeckt
- N1 → N3 (False): Abgedeckt
- N3 → N4 (True): Abgedeckt
- N3 → N5 (False): NICHT abgedeckt
Branch Coverage (C1):
\(\text{C1} = \frac{3}{4} = 75\%\)
Fehlender Test
Um 100% zu erreichen, brauchen wir:
def test_zero():
assert classify_number(0) == "zero"
Dies deckt ab:
- N5 (die “zero”-Rückgabe)
- N3 → N5 Kante (False-Branch von
x < 0)
Übung 3: calculate_grade() - Mehrere Branches
Code
def calculate_grade(score):
if score >= 90: # Node 1
return "A" # Node 2
elif score >= 80: # Node 3
return "B" # Node 4
elif score >= 70: # Node 5
return "C" # Node 6
elif score >= 60: # Node 7
return "D" # Node 8
else:
return "F" # Node 9
Kontrollflussgraph
flowchart TD
START([Start]) --> N1{score >= 90?}
N1 -->|True| N2[return 'A']
N1 -->|False| N3{score >= 80?}
N3 -->|True| N4[return 'B']
N3 -->|False| N5{score >= 70?}
N5 -->|True| N6[return 'C']
N5 -->|False| N7{score >= 60?}
N7 -->|True| N8[return 'D']
N7 -->|False| N9[return 'F']
N2 --> END([End])
N4 --> END
N6 --> END
N8 --> END
N9 --> END
Testfälle
def test_grade_A():
assert calculate_grade(95) == "A"
def test_grade_B():
assert calculate_grade(85) == "B"
Fragen
- Verfolge beide Tests und markiere abgedeckte Knoten/Kanten
- Berechne C0 und C1
- Wie viele weitere Tests werden für 100% C1 benötigt?
Lösung anzeigen
Ablaufverfolgung: test_grade_A (score = 95)
Pfad: START → N1 (True) → N2 → END
Abgedeckte Knoten: N1, N2
Ablaufverfolgung: test_grade_B (score = 85)
Pfad: START → N1 (False) → N3 (True) → N4 → END
Abgedeckte Knoten: N1, N3, N4
Kombinierte Coverage
Abgedeckte Knoten: N1, N2, N3, N4 (4 Knoten) NICHT abgedeckte Knoten: N5, N6, N7, N8, N9 (5 Knoten)
Statement Coverage (C0):
\(\text{C0} = \frac{4}{9} = 44\%\)
Branches (Entscheidungsergebnisse):
| Entscheidung | True-Kante | False-Kante |
|---|---|---|
| N1 (score >= 90) | Abgedeckt | Abgedeckt |
| N3 (score >= 80) | Abgedeckt | Nicht abgedeckt |
| N5 (score >= 70) | Nicht abgedeckt | Nicht abgedeckt |
| N7 (score >= 60) | Nicht abgedeckt | Nicht abgedeckt |
Abgedeckt: 3 von 8 Branches
Branch Coverage (C1):
\(\text{C1} = \frac{3}{8} = 37{,}5\%\)
Benötigte Tests für 100% C1
Wir müssen alle 8 Branch-Ergebnisse abdecken. Aktuell fehlen:
- N3 False → Braucht score < 80 aber >= 70 (z.B. 75)
- N5 True → Bereits durch score 75 abgedeckt
- N5 False → Braucht score < 70 aber >= 60 (z.B. 65)
- N7 True → Bereits durch score 65 abgedeckt
- N7 False → Braucht score < 60 (z.B. 50)
Minimum zusätzlich benötigte Tests: 3
def test_grade_C():
assert calculate_grade(75) == "C"
def test_grade_D():
assert calculate_grade(65) == "D"
def test_grade_F():
assert calculate_grade(50) == "F"
Übung 4: C0 vs C1 Vergleich
Code
def check_eligibility(age, has_license):
if age >= 18: # Node 1
if has_license: # Node 2
return "approved" # Node 3
return "need license" # Node 4
return "too young" # Node 5
Kontrollflussgraph
flowchart TD
START([Start]) --> N1{age >= 18?}
N1 -->|True| N2{has_license?}
N1 -->|False| N5[return 'too young']
N2 -->|True| N3[return 'approved']
N2 -->|False| N4[return 'need license']
N3 --> END([End])
N4 --> END
N5 --> END
Testfälle
def test_approved():
assert check_eligibility(25, True) == "approved"
def test_need_license():
assert check_eligibility(20, False) == "need license"
Fragen
- Was ist C0 für diese beiden Tests?
- Was ist C1 für diese beiden Tests?
- Ist C0 > C1, C0 < C1, oder C0 == C1?
- Was sagt dir das über die Beziehung zwischen C0 und C1?
Lösung anzeigen
Ablaufverfolgung: test_approved (age=25, has_license=True)
Pfad: START → N1 (True) → N2 (True) → N3 → END
Abgedeckte Knoten: N1, N2, N3
Ablaufverfolgung: test_need_license (age=20, has_license=False)
Pfad: START → N1 (True) → N2 (False) → N4 → END
Abgedeckte Knoten: N1, N2, N4
Kombinierte Knotenabdeckung
Abgedeckt: N1, N2, N3, N4 (4 von 5) NICHT abgedeckt: N5 (too young Pfad)
Statement Coverage (C0):
\(\text{C0} = \frac{4}{5} = 80\%\)
Branch Coverage
| Entscheidung | True | False |
|---|---|---|
| N1 (age >= 18) | Abgedeckt | Nicht abgedeckt |
| N2 (has_license) | Abgedeckt | Abgedeckt |
Abgedeckt: 3 von 4 Branches
Branch Coverage (C1):
\(\text{C1} = \frac{3}{4} = 75\%\)
Vergleich
C0 (80%) > C1 (75%)
Dies demonstriert eine wichtige Erkenntnis:
Du kannst höhere Statement Coverage als Branch Coverage erreichen!
Hier ist der Grund:
- Beide Tests nehmen den N1 True Branch (age >= 18)
- Das deckt 4 Anweisungen ab (N1, N2, N3, N4)
- Aber der N1 False Branch wird nie genommen
- Die N5 Anweisung (im False Branch) wird nie ausgeführt
Das zeigt, warum C1 stärker als C0 ist:
- 100% C1 → 100% C0 (garantiert)
- 100% C0 garantiert NICHT 100% C1
Fehlender Test
def test_too_young():
assert check_eligibility(16, True) == "too young"
Dies deckt ab:
- N5 (die fehlende Anweisung)
- N1 → N5 Kante (der fehlende False Branch)
Übung 5: Verschachtelte Bedingungen
Code
def validate_password(password):
if len(password) < 8: # Node 1
return "too short" # Node 2
has_upper = any(c.isupper() for c in password) # Node 3
has_digit = any(c.isdigit() for c in password) # Node 4
if not has_upper: # Node 5
return "need uppercase" # Node 6
if not has_digit: # Node 7
return "need digit" # Node 8
return "valid" # Node 9
Kontrollflussgraph
flowchart TD
START([Start]) --> N1{len < 8?}
N1 -->|True| N2[return 'too short']
N1 -->|False| N3[has_upper = ...]
N3 --> N4[has_digit = ...]
N4 --> N5{not has_upper?}
N5 -->|True| N6[return 'need uppercase']
N5 -->|False| N7{not has_digit?}
N7 -->|True| N8[return 'need digit']
N7 -->|False| N9[return 'valid']
N2 --> END([End])
N6 --> END
N8 --> END
N9 --> END
Testfall
def test_valid_password():
assert validate_password("SecurePass1") == "valid"
Fragen
- Verfolge den Test durch den CFG
- Berechne C0
- Berechne C1
- Liste ALLE Testfälle auf, die für 100% C1 benötigt werden
Lösung anzeigen
Ablaufverfolgung: test_valid_password (password = “SecurePass1”)
- N1: len(“SecurePass1”) = 11, 11 < 8 ist False → Gehe zu N3
- N3: has_upper = True (hat ‘S’ und ‘P’)
- N4: has_digit = True (hat ‘1’)
- N5: not has_upper = not True = False → Gehe zu N7
- N7: not has_digit = not True = False → Gehe zu N9
- N9: return “valid”
Pfad: START → N1 (F) → N3 → N4 → N5 (F) → N7 (F) → N9 → END
Abgedeckte Knoten
Abgedeckt: N1, N3, N4, N5, N7, N9 (6 Knoten) NICHT abgedeckt: N2, N6, N8 (3 Knoten)
Statement Coverage (C0):
\(\text{C0} = \frac{6}{9} = 67\%\)
Abgedeckte Branches
| Entscheidung | True | False |
|---|---|---|
| N1 (len < 8) | Nicht abgedeckt | Abgedeckt |
| N5 (not has_upper) | Nicht abgedeckt | Abgedeckt |
| N7 (not has_digit) | Nicht abgedeckt | Abgedeckt |
Abgedeckt: 3 von 6 Branches
Branch Coverage (C1):
\(\text{C1} = \frac{3}{6} = 50\%\)
Tests für 100% C1
Wir müssen den True Branch jeder Entscheidung abdecken:
1. N1 True (Passwort zu kurz):
def test_too_short():
assert validate_password("Short1") == "too short" # len = 6
2. N5 True (Großbuchstabe fehlt):
def test_need_uppercase():
assert validate_password("lowercase1") == "need uppercase"
3. N7 True (Ziffer fehlt):
def test_need_digit():
assert validate_password("NoDigitHere") == "need digit"
Vollständige Testsuite für 100% C1:
def test_valid():
assert validate_password("SecurePass1") == "valid"
def test_too_short():
assert validate_password("Short1") == "too short"
def test_need_uppercase():
assert validate_password("lowercase1") == "need uppercase"
def test_need_digit():
assert validate_password("NoDigitHere") == "need digit"
Übung 6: Early Returns
Code
def calculate_discount(amount, is_member, coupon_code):
if amount <= 0: # Node 1
return 0 # Node 2
discount = 0 # Node 3
if is_member: # Node 4
discount += 10 # Node 5
if coupon_code == "SAVE20": # Node 6
discount += 20 # Node 7
elif coupon_code == "SAVE10": # Node 8
discount += 10 # Node 9
return amount * (discount / 100) # Node 10
Kontrollflussgraph
flowchart TD
START([Start]) --> N1{amount <= 0?}
N1 -->|True| N2[return 0]
N1 -->|False| N3[discount = 0]
N3 --> N4{is_member?}
N4 -->|True| N5[discount += 10]
N4 -->|False| N6a[skip]
N5 --> N6{coupon == 'SAVE20'?}
N6a --> N6
N6 -->|True| N7[discount += 20]
N6 -->|False| N8{coupon == 'SAVE10'?}
N7 --> N10[return calculated]
N8 -->|True| N9[discount += 10]
N8 -->|False| N10
N9 --> N10
N2 --> END([End])
N10 --> END
Testfälle
def test_member_with_save20():
result = calculate_discount(100, True, "SAVE20")
assert result == 30 # 10% member + 20% coupon
def test_non_member_no_coupon():
result = calculate_discount(100, False, None)
assert result == 0 # No discounts
Fragen
- Verfolge beide Tests durch den CFG
- Welche Branches sind noch nicht abgedeckt?
- Wie viele Tests werden für 100% C1 benötigt?
Lösung anzeigen
Ablaufverfolgung: test_member_with_save20
Eingabe: amount=100, is_member=True, coupon_code=”SAVE20”
Pfad: N1(F) → N3 → N4(T) → N5 → N6(T) → N7 → N10 → END
Abgedeckt: N1, N3, N4, N5, N6, N7, N10
Ablaufverfolgung: test_non_member_no_coupon
Eingabe: amount=100, is_member=False, coupon_code=None
Pfad: N1(F) → N3 → N4(F) → N6(F) → N8(F) → N10 → END
Abgedeckt: N1, N3, N4, N6, N8, N10
Kombinierte Coverage
Abgedeckte Knoten: N1, N3, N4, N5, N6, N7, N8, N10 (8 Knoten) NICHT abgedeckte Knoten: N2, N9 (2 Knoten)
C0: 8/10 = 80%
Branches:
| Entscheidung | True | False |
|---|---|---|
| N1 (amount <= 0) | Nein | Ja |
| N4 (is_member) | Ja | Ja |
| N6 (coupon == SAVE20) | Ja | Ja |
| N8 (coupon == SAVE10) | Nein | Ja |
C1: 6/8 = 75%
Fehlende Tests für 100% C1
1. N1 True (negativer/null Betrag):
def test_invalid_amount():
assert calculate_discount(0, True, "SAVE20") == 0
2. N8 True (SAVE10 Gutschein):
def test_save10_coupon():
result = calculate_discount(100, False, "SAVE10")
assert result == 10
Minimum Tests für 100% C1: 4 insgesamt
Übung 7: Komplexe Bedingungen (Fortgeschritten)
Code
def approve_loan(income, credit_score, has_collateral):
# Automatic rejection
if income < 30000: # Node 1
return "rejected: low income" # Node 2
if credit_score < 600: # Node 3
return "rejected: poor credit" # Node 4
# Premium approval
if income >= 100000 and credit_score >= 750: # Node 5
return "approved: premium" # Node 6
# Standard approval with collateral
if credit_score >= 700 or has_collateral: # Node 7
return "approved: standard" # Node 8
return "manual review required" # Node 9
Kontrollflussgraph
flowchart TD
START([Start]) --> N1{income < 30k?}
N1 -->|True| N2[rejected: low income]
N1 -->|False| N3{credit < 600?}
N3 -->|True| N4[rejected: poor credit]
N3 -->|False| N5{income>=100k AND credit>=750?}
N5 -->|True| N6[approved: premium]
N5 -->|False| N7{credit>=700 OR collateral?}
N7 -->|True| N8[approved: standard]
N7 -->|False| N9[manual review]
N2 --> END([End])
N4 --> END
N6 --> END
N8 --> END
N9 --> END
Testsuite
def test_premium_approval():
assert approve_loan(150000, 800, False) == "approved: premium"
def test_standard_with_good_credit():
assert approve_loan(50000, 720, False) == "approved: standard"
def test_standard_with_collateral():
assert approve_loan(50000, 650, True) == "approved: standard"
Fragen
- Berechne C0 und C1 für diese Testsuite
- Welche Entscheidungsergebnisse sind NICHT abgedeckt?
- Entwirf zusätzliche Tests um 100% C1 zu erreichen
Lösung anzeigen
Alle Tests verfolgen
test_premium_approval (income=150000, credit=800, collateral=False):
N1(F) → N3(F) → N5(T) → N6 → END
test_standard_with_good_credit (income=50000, credit=720, collateral=False):
N1(F) → N3(F) → N5(F) → N7(T) → N8 → END
test_standard_with_collateral (income=50000, credit=650, collateral=True):
N1(F) → N3(F) → N5(F) → N7(T) → N8 → END
Coverage-Analyse
Abgedeckte Knoten: N1, N3, N5, N6, N7, N8 (6 Knoten) NICHT abgedeckte Knoten: N2, N4, N9 (3 Knoten)
C0: 6/9 = 67%
Branches:
| Entscheidung | True | False |
|---|---|---|
| N1 (income < 30k) | Nein | Ja |
| N3 (credit < 600) | Nein | Ja |
| N5 (income>=100k AND credit>=750) | Ja | Ja |
| N7 (credit>=700 OR collateral) | Ja | Nein |
C1: 5/8 = 62,5%
Nicht abgedeckte Entscheidungsergebnisse
- N1 True: Einkommen unter 30k
- N3 True: Kredit-Score unter 600
- N7 False: Kredit-Score < 700 UND keine Sicherheit
Zusätzliche Tests für 100% C1
def test_rejected_low_income():
# Deckt N1 True, N2 ab
assert approve_loan(20000, 750, True) == "rejected: low income"
def test_rejected_poor_credit():
# Deckt N3 True, N4 ab
assert approve_loan(50000, 500, True) == "rejected: poor credit"
def test_manual_review():
# Deckt N7 False, N9 ab
# Braucht: income >= 30k, credit >= 600 aber < 700, keine Sicherheit
assert approve_loan(50000, 650, False) == "manual review required"
Vollständige Testsuite für 100% C1: 6 Tests
Übung 8: Entwirf deine eigenen Tests
Code
def process_order(total, user_type, promo_code):
if total <= 0: # Node 1
return "invalid order" # Node 2
base_discount = 0 # Node 3
if user_type == "premium": # Node 4
base_discount = 15 # Node 5
elif user_type == "member": # Node 6
base_discount = 5 # Node 7
if promo_code == "FLASH50": # Node 8
if total >= 100: # Node 9
return f"discount: 50%" # Node 10
return "promo requires $100+" # Node 11
if base_discount > 0: # Node 12
return f"discount: {base_discount}%" # Node 13
return "no discount" # Node 14
Herausforderung
Ohne einen fertigen CFG oder Tests, entwirf eine Testsuite, die erreicht:
- 100% Statement Coverage (C0)
- 100% Branch Coverage (C1)
Fragen
- Zeichne den CFG (auf Papier oder verfolge ihn gedanklich)
- Liste alle Entscheidungspunkte und ihre Branches auf
- Entwirf die minimale Testsuite für 100% C1
- Verifiziere deine Coverage indem du jeden Test verfolgst
Lösung anzeigen
Entscheidungspunkte und Branches
| Knoten | Entscheidung | True Branch | False Branch |
|---|---|---|---|
| N1 | total <= 0 | N2 | N3 |
| N4 | user_type == “premium” | N5 | N6 |
| N6 | user_type == “member” | N7 | N8 |
| N8 | promo_code == “FLASH50” | N9 | N12 |
| N9 | total >= 100 | N10 | N11 |
| N12 | base_discount > 0 | N13 | N14 |
Gesamte Branches: 12
Minimale Testsuite für 100% C1
# Test 1: Ungültige Bestellung (N1 True)
def test_invalid_order():
assert process_order(0, "guest", None) == "invalid order"
# Test 2: Premium-Nutzer, keine Promo (N4 True, N8 False, N12 True)
def test_premium_no_promo():
assert process_order(50, "premium", None) == "discount: 15%"
# Test 3: Mitglied-Nutzer, keine Promo (N4 False, N6 True, N12 True)
def test_member_no_promo():
assert process_order(50, "member", None) == "discount: 5%"
# Test 4: Gast, FLASH50 Promo, hoher Betrag (N4 False, N6 False, N8 True, N9 True)
def test_flash50_high_total():
assert process_order(150, "guest", "FLASH50") == "discount: 50%"
# Test 5: Gast, FLASH50 Promo, niedriger Betrag (N8 True, N9 False)
def test_flash50_low_total():
assert process_order(50, "guest", "FLASH50") == "promo requires $100+"
# Test 6: Gast, keine Promo (N8 False, N12 False)
def test_guest_no_promo():
assert process_order(50, "guest", None) == "no discount"
Benötigte Tests insgesamt: 6
Verifizierung
Verfolge jeden Test, um zu bestätigen, dass alle 14 Knoten und 12 Branches abgedeckt sind:
| Test | Abgedeckte Knoten | Neue Branches |
|---|---|---|
| 1 | N1, N2 | N1→N2 |
| 2 | N1, N3, N4, N5, N8, N12, N13 | N1→N3, N4→N5, N8→N12, N12→N13 |
| 3 | N1, N3, N4, N6, N7, N8, N12, N13 | N4→N6, N6→N7 |
| 4 | N1, N3, N4, N6, N8, N9, N10 | N6→N8, N8→N9, N9→N10 |
| 5 | N1, N3, N4, N6, N8, N9, N11 | N9→N11 |
| 6 | N1, N3, N4, N6, N8, N12, N14 | N12→N14 |
Alle 14 Knoten abgedeckt: C0 = 100% Alle 12 Branches abgedeckt: C1 = 100%
Zusammenfassung
Was du gelernt hast
Nach Abschluss dieser Übungen solltest du in der Lage sein:
- Kontrollflussgraphen zu lesen und zu interpretieren
- Testfallausführung durch Code-Pfade zu verfolgen
- Statement Coverage (C0) durch Zählen abgedeckter Knoten zu berechnen
- Branch Coverage (C1) durch Zählen abgedeckter Kanten zu berechnen
- Tests zu entwerfen, um spezifische Coverage-Ziele zu erreichen
- Zu verstehen, warum C1 C0 subsumiert
Wichtige Formeln
Statement Coverage (C0):
\(\text{C0} = \frac{\text{Ausgeführte Knoten}}{\text{Gesamte Knoten}} \times 100\%\)
Branch Coverage (C1):
\(\text{C1} = \frac{\text{Genommene Kanten}}{\text{Gesamte Entscheidungskanten}} \times 100\%\)
Häufige Muster
- Early Returns erzeugen oft nicht abgedeckte Branches
- Verschachtelte Bedingungen erfordern Tests für jede Kombination
- Ein Testpfad deckt normalerweise ~30-50% der Branches ab
- 100% C1 benötigt typischerweise einen Test pro eindeutigem Ergebnis
Was kommt als nächstes?
Fahre mit der Coverage Detective Übung fort, um das Analysieren echter pytest-cov Reports und das Entwerfen von Tests zur Abdeckung fehlenden Codes zu üben.