Home

03 Grundlagen des Testens: CFG Tracing Übung

exercise coverage cfg control-flow c0 c1 testing chapter-03

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.

Interaktive Übung starten →

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:

Fokusgebiete: CFGs lesen, Pfade verfolgen, C0 und C1 berechnen


Lernziele:

Anleitung:

  1. Studiere den Code und seinen zugehörigen CFG
  2. Verfolge die gegebenen Testfälle durch den Graphen
  3. Identifiziere, welche Knoten und Kanten abgedeckt werden
  4. Berechne die Coverage-Prozentsätze
  5. 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

  1. Welche Knoten werden von diesem Test abgedeckt?
  2. Welche Kanten werden von diesem Test abgedeckt?
  3. Was ist die Statement Coverage (C0)?
  4. Was ist die Branch Coverage (C1)?
Lösung anzeigen

Ablaufverfolgung

Mit Eingabe x = 5:

  1. START → N1: Führe if x > 0 aus → 5 > 0 ist True
  2. N1 → N2: Nimm True-Kante, führe return "pos" aus
  3. 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 Fall
  • x < 0 → Negativer Fall
  • x == 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

  1. Verfolge beide Tests durch den CFG
  2. Was ist die kombinierte C0?
  3. Was ist die kombinierte C1?
  4. 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

  1. Verfolge beide Tests und markiere abgedeckte Knoten/Kanten
  2. Berechne C0 und C1
  3. 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

  1. Was ist C0 für diese beiden Tests?
  2. Was ist C1 für diese beiden Tests?
  3. Ist C0 > C1, C0 < C1, oder C0 == C1?
  4. 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

  1. Verfolge den Test durch den CFG
  2. Berechne C0
  3. Berechne C1
  4. Liste ALLE Testfälle auf, die für 100% C1 benötigt werden
Lösung anzeigen

Ablaufverfolgung: test_valid_password (password = “SecurePass1”)

  1. N1: len(“SecurePass1”) = 11, 11 < 8 ist False → Gehe zu N3
  2. N3: has_upper = True (hat ‘S’ und ‘P’)
  3. N4: has_digit = True (hat ‘1’)
  4. N5: not has_upper = not True = False → Gehe zu N7
  5. N7: not has_digit = not True = False → Gehe zu N9
  6. 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

  1. Verfolge beide Tests durch den CFG
  2. Welche Branches sind noch nicht abgedeckt?
  3. 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

  1. Berechne C0 und C1 für diese Testsuite
  2. Welche Entscheidungsergebnisse sind NICHT abgedeckt?
  3. 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

  1. N1 True: Einkommen unter 30k
  2. N3 True: Kredit-Score unter 600
  3. 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:

  1. 100% Statement Coverage (C0)
  2. 100% Branch Coverage (C1)

Fragen

  1. Zeichne den CFG (auf Papier oder verfolge ihn gedanklich)
  2. Liste alle Entscheidungspunkte und ihre Branches auf
  3. Entwirf die minimale Testsuite für 100% C1
  4. 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:

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

  1. Early Returns erzeugen oft nicht abgedeckte Branches
  2. Verschachtelte Bedingungen erfordern Tests für jede Kombination
  3. Ein Testpfad deckt normalerweise ~30-50% der Branches ab
  4. 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.

© 2026 Dominik Mueller   •  Powered by Soopr   •  Theme  Moonwalk