Hinweis zur Privatsphäre: Wenn Sie sich mit Ihrem GitHub-Konto anmelden, werden Ihre Quiz-Versuche in Google Firebase gespeichert, damit Sie Ihren Fortschritt verfolgen können. Diese Daten sind nur für den Kursleiter einsehbar, werden nicht anderweitig weitergegeben und nach Semesterende gelöscht. Frühere Löschung auf Anfrage beim Kursleiter. Möchten Sie anonym bleiben? Nutzen Sie den Gastmodus oder ein GitHub-Konto ohne Ihren echten Namen.
Lädt...

03 Übung: Coverage Detektiv

Punkte: 0 / 10

Anleitung

In dieser Übung wirst du zum Coverage-Detektiv. Du analysierst echte pytest-cov-Ausgaben, identifizierst welche Code-Pfade ungetestet sind und entwirfst Tests, um die Lücken zu schließen.


Vorbereitung: Zuerst lesen

Bevor du diese Übung versuchst, lies die folgenden Vorlesungsabschnitte:

Aus Kapitel 03 (TDD und CI): TDD und CI:

Aus Kapitel 03 (Testtheorie und Coverage): Testtheorie & Coverage:


Lernziele

  • pytest-cov-Ausgabe interpretieren und fehlende Zeilen identifizieren
  • Verstehen, warum bestimmte Zeilen nicht ausgeführt wurden
  • Testfälle entwerfen, die spezifische Branches abdecken
  • Testfunktionen schreiben, die Ziel-Coverage erreichen

Struktur

  • Teil 1 (Fragen 1-5): Analyse - Identifizieren was fehlt und warum
  • Teil 2 (Fragen 6-10): Entwurf - Tests erstellen, um die Lücken zu schließen

Bewertungsleitfaden

  • 9-10 richtig: Ausgezeichnet! Bereit für die GitHub Classroom-Aufgabe
  • 7-8 richtig: Gutes Verständnis! Lies die Erklärungen
  • 5-6 richtig: Ausreichendes Verständnis. Schau dir die Vorlesungsmaterialien an
  • Unter 5: Bitte wiederhole die Coverage-Konzepte bevor du fortfährst

GitHub Classroom-Aufgabe

Nach Abschluss dieses Quiz wende deine Fähigkeiten in einer echten praktischen Aufgabe an:

Coverage-Verbesserungsübung akzeptieren

Du wirst Coverage von ~54% auf 85%+ verbessern mit sofortigem CI-Feedback!

Frage1 Teil 1: Analyse

Code: validate_age.py

1: def validate_age(age):
2:     if age < 0:
3:         return "invalid: negative"
4:     if age < 18:
5:         return "minor"
6:     if age < 65:
7:         return "adult"
8:     return "senior"

Tests:

def test_minor():
    assert validate_age(15) == "minor"

def test_adult():
    assert validate_age(30) == "adult"

Coverage-Report: Missing: 3, 8

Welche Bedingungen sind NICHT abgedeckt, und warum?

Frage2 Teil 1: Analyse

Code: format_name.py

1: def format_name(first, last, formal=False):
2:     if not first or not last:
3:         return "Invalid name"
4:
5:     if formal:
6:         return f"Mr./Ms. {last}"
7:     return f"{first} {last}"

Tests:

def test_informal_name():
    assert format_name("John", "Doe") == "John Doe"

def test_formal_name():
    assert format_name("Jane", "Smith", formal=True) == "Mr./Ms. Smith"

Coverage-Report: Missing: 3

Zeile 3 ist return "Invalid name". Warum ist sie NICHT abgedeckt?

Frage3 Teil 1: Analyse

Code: check_access.py

1:  def check_access(user_role, resource_type, is_owner):
2:      # Admin can access anything
3:      if user_role == "admin":
4:          return True
5:
6:      # Owner can access their own resources
7:      if is_owner:
8:          return True
9:
10:     # Editors can access documents and images
11:     if user_role == "editor" and resource_type in ["document", "image"]:
12:         return True
13:
14:     return False

Tests:

def test_admin_access():
    assert check_access("admin", "secret", False) == True

def test_owner_access():
    assert check_access("viewer", "document", True) == True

def test_editor_document():
    assert check_access("editor", "document", False) == True

def test_denied():
    assert check_access("viewer", "document", False) == False

Coverage-Report: Missing: 12 (teilweise)

Zeile 11-12 ist als teilweise abgedeckt markiert. Warum?

Frage4 Teil 1: Analyse

Code: divide.py

1: def safe_divide(a, b):
2:     try:
3:         result = a / b
4:     except ZeroDivisionError:
5:         return None
6:     except TypeError:
7:         return "invalid input"
8:     return result

Tests:

def test_normal_division():
    assert safe_divide(10, 2) == 5.0

def test_divide_by_zero():
    assert safe_divide(10, 0) is None

Coverage-Report: Missing: 7, 8

Zeilen 6-7 sind der except TypeError-Block. Welche Eingabe würde diese Exception auslösen?

Frage5 Teil 1: Analyse

Code: find_max.py

1: def find_max(numbers):
2:     if not numbers:
3:         return None
4:
5:     max_val = numbers[0]
6:     for num in numbers[1:]:
7:         if num > max_val:
8:             max_val = num
9:     return max_val

Tests:

def test_single_element():
    assert find_max([42]) == 42

def test_sorted_ascending():
    assert find_max([1, 2, 3, 4, 5]) == 5

Coverage-Report: Missing: 3

Zeile 3 ist return None. Warum ist die Schleife (Zeilen 6-8) als abgedeckt angezeigt, aber Zeile 3 nicht?

Frage6 Teil 2: Tests Entwerfen

Code: validate_password.py

1:  def validate_password(password):
2:      if len(password) < 8:
3:          return "too short"
4:
5:      if len(password) > 128:
6:          return "too long"
7:
8:      has_upper = any(c.isupper() for c in password)
9:      has_lower = any(c.islower() for c in password)
10:     has_digit = any(c.isdigit() for c in password)
11:
12:     if not has_upper:
13:         return "needs uppercase"
14:     if not has_lower:
15:         return "needs lowercase"
16:     if not has_digit:
17:         return "needs digit"
18:
19:     return "valid"

Coverage-Report: Missing: 6, 15, 17

Welcher Test würde Zeile 15 (return "needs lowercase") abdecken?

Frage7 Teil 2: Tests Entwerfen

Code: shipping.py

1:  def calculate_shipping(weight, destination, express=False):
2:      if weight <= 0:
3:          raise ValueError("Weight must be positive")
4:
5:      if destination not in ["domestic", "international"]:
6:          raise ValueError("Invalid destination")
7:
8:      base_rate = 5.0 if destination == "domestic" else 15.0
9:      weight_charge = weight * 0.5
10:
11:     subtotal = base_rate + weight_charge
12:
13:     if express:
14:         subtotal *= 1.5
15:
16:     if subtotal > 50:
17:         discount = subtotal * 0.1
18:         return subtotal - discount
19:
20:     return subtotal

Coverage-Report: Missing: 3, 6, 17, 18

Um Zeilen 17-18 (Rabatt für subtotal > 50) abzudecken, was ist das MINIMALE Gewicht für domestic non-express?

Frage8 Teil 2: Tests Entwerfen

Code: config.py

1:  def parse_config(config_string):
2:      if not config_string:
3:          return {}
4:
5:      result = {}
6:      lines = config_string.strip().split('\n')
7:
8:      for line in lines:
9:          line = line.strip()
10:
11:         if not line or line.startswith('#'):
12:             continue
13:
14:         if '=' not in line:
15:             raise ValueError(f"Invalid config line: {line}")
16:
17:         key, value = line.split('=', 1)
18:         key = key.strip()
19:         value = value.strip()
20:
21:         if not key:
22:             raise ValueError("Empty key not allowed")
23:
24:         result[key] = value
25:
26:     return result

Coverage-Report: Missing: 3, 12, 15, 22

Welche Eingabe würde Zeile 22 (raise ValueError("Empty key not allowed")) abdecken?

Frage9 Teil 2: Tests Entwerfen

Code: discount.py

1:  def calculate_discount(amount, is_member, coupon_code):
2:      if amount <= 0:
3:          return 0
4:
5:      discount = 0.0
6:
7:      if is_member:
8:          discount += 0.10  # 10% Mitgliederrabatt
9:
10:     if coupon_code is not None:
11:         if coupon_code == "SAVE20":
12:             discount += 0.20
13:         elif coupon_code == "SAVE10":
14:             discount += 0.10
15:         elif coupon_code.startswith("VIP"):
16:             if is_member:
17:                 discount += 0.30
18:             else:
19:                 discount += 0.15
20:
21:     # Rabatt auf 50% begrenzen
22:     if discount > 0.50:
23:         discount = 0.50
24:
25:     return amount * discount

Coverage-Report: Missing: 3, 14, 17, 19, 23

Kann Zeile 23 (Rabatt-Cap = 0.50) mit diesem Code jemals erreicht werden?

Frage10 Teil 2: Tests Entwerfen

Code: process_order.py

1:  def process_order(items, customer_type, promo_code=None):
2:      if not items:
3:          return {"status": "error", "message": "No items"}
4:
5:      subtotal = sum(item["price"] * item["quantity"] for item in items)
6:
7:      if subtotal <= 0:
8:          return {"status": "error", "message": "Invalid total"}
9:
10:     discount_rate = 0
11:
12:     if customer_type == "premium":
13:         discount_rate = 0.15
14:     elif customer_type == "member":
15:         discount_rate = 0.05
16:
17:     if promo_code == "FLASH50" and subtotal >= 100:
18:         discount_rate += 0.50
19:     elif promo_code == "FLASH25" and subtotal >= 50:
20:         discount_rate += 0.25
21:
22:     if discount_rate > 0.60:
23:         discount_rate = 0.60
24:
25:     discount = subtotal * discount_rate
26:     total = subtotal - discount
27:
28:     return {"status": "success", ...}

Um Zeile 23 (Rabatt-Cap bei 60%) auszulösen, welche Kombination funktioniert?

© 2026 Dominik Mueller   •  Powered by Soopr   •  Theme  Moonwalk