Home

03 Grundlagen des Testens: Basics

lecture testing pytest unit-testing test-pyramid aaa-pattern clean-code test-frameworks

1. Einführung: Ihre CI ist grün, aber funktioniert Ihr Code?

Nach Kapitel 02 (Refactoring) hat Ihr Road Profile Viewer eine schöne modulare Struktur:

src/road_profile_viewer/
├── __init__.py
├── geometry.py      # Reine Mathe-Funktionen
├── road.py          # Straßengenerierung
├── visualization.py # Dash UI
└── main.py          # Einstiegspunkt

Ihre CI-Pipeline validiert jeden Pull Request:

✅ Ruff check (PEP8-Stil)
✅ Ruff format check
✅ Pyright (Type Hints)

Alle Checks grün. Code gemerged. Deployed in Produktion.

Dann passiert das:

# Ein Benutzer gibt einen vertikalen Kamerawinkel ein
angle = 90.0

# Ihr Code stürzt ab:
def find_intersection(x_road, y_road, angle_degrees, ...):
    angle_rad = -np.deg2rad(angle_degrees)
    slope = np.tan(angle_rad)  # tan(90°) = ∞
    # ZeroDivisionError or infinite loop!

Ihre CI sagte, nichts sei falsch. Weil:

Das ist die Lücke, die wir heute füllen.


2. Lernziele

Am Ende dieser Vorlesung werden Sie:

  1. Den Unterschied verstehen zwischen Code-Qualität (Stil) und Code-Korrektheit (Logik)
  2. Die Testing-Pyramide meistern - Unit-, Modul- und End-to-End-Tests
  3. Effektive Unit-Tests schreiben mit Äquivalenzklassen und Boundary-Analyse
  4. Pytest verwenden, um Ihren modularen Python-Code zu testen
  5. LLMs nutzen, um Test-Schreiben zu beschleunigen (während Sie ihre Grenzen verstehen)
  6. Das “Test-Kegel”-Muster brechen, das Entwickler vom Testen abhält
  7. Feature-Branch-Workflow anwenden, um Tests zu Ihrem Projekt hinzuzufügen

Was Sie lernen WERDEN:

Was Sie NICHT (noch) lernen werden:


3. Teil 1: Warum Testing wichtig ist

3.1 Der Bug, den CI übersehen hat

Schauen wir uns einen echten Bug in Ihrem src/road_profile_viewer/geometry.py an:

from numpy.typing import NDArray
import numpy as np

def find_intersection(
    x_road: NDArray[np.float64],
    y_road: NDArray[np.float64],
    angle_degrees: float,
    camera_x: float = 0,
    camera_y: float = 1.5,
) -> tuple[float | None, float | None, float | None]:
    """Find intersection between camera ray and road profile."""
    angle_rad = -np.deg2rad(angle_degrees)

    # Handle vertical ray
    if np.abs(np.cos(angle_rad)) < 1e-10:
        return None, None, None  # Currently returns None for vertical angles

    slope = np.tan(angle_rad)
    # ... rest of function

Was passiert mit verschiedenen Winkeln?

Winkel Erwartetes Verhalten Tatsächliches Verhalten CI-Status
-10° Finde Schnittpunkt ✅ Funktioniert ✅ Grün
-45° Finde Schnittpunkt ✅ Funktioniert ✅ Grün
Horizontaler Strahl ✅ Funktioniert ✅ Grün
90° Vertikaler Strahl ❌ Gibt None zurück (sollte elegant behandeln) ✅ Grün
Empty array Leere Daten behandeln Stürzt mit IndexError ab ✅ Grün

CI ist blind für Logik-Bugs. Es prüft nur:

Es prüft nicht:

3.2 Code-Qualität ≠ Code-Korrektheit

Code-Qualität-Tools (Kapitel 02 (Code-Qualität in der Praxis))

Tests (Diese Vorlesung)

Sie brauchen BEIDES:

Code-Qualität-Tools → "Sieht das professionell aus?"
Tests               → "Funktioniert das tatsächlich?"

4. Teil 2: Die Testing-Pyramide

4.1 Was ist die Testing-Pyramide?

Die Testing-Pyramide ist eine Strategie zum Ausgleichen verschiedener Testtypen:

       /\
      /E2E\       ← Wenige, langsam, teuer
     /------\       (Volle Dash-App, Browser-Simulation)
    /Module \     ← Einige, moderate Geschwindigkeit
   /----------\     (Mehrere Module zusammenarbeiten)
  /   Unit     \   ← Viele, schnell, günstig
 /--------------\    (Einzelne Funktionen)

Drei Ebenen des Testens:

  1. Unit-Tests (Basis der Pyramide)
    • Testen einzelne Funktionen isoliert
    • Schnell (Millisekunden)
    • Einfach zu schreiben und zu warten
    • Fangen Bugs früh
  2. Modul-/Integrationstests (Mitte der Pyramide)
    • Testen mehrere Module, die zusammenarbeiten
    • Moderate Geschwindigkeit (Sekunden)
    • Fangen Schnittstellendiskrepanzen
  3. End-to-End-Tests (Spitze der Pyramide)
    • Testen den gesamten Anwendungsfluss
    • Langsam (Sekunden bis Minuten)
    • Teuer zu warten
    • Fangen Probleme auf Systemebene

4.2 Warum die Pyramidenform?

Verhältnis-Empfehlung:

Warum?

Geschwindigkeit zählt für die Feedback-Schleife:

Unit-Test:    find_intersection()-Test läuft in 0.01 Sekunden
Modul-Test:   Geometrie + Straße zusammen läuft in 0.1 Sekunden
E2E-Test:     Dash-App starten, Klicks simulieren läuft in 5+ Sekunden

Wenn Sie eine Zeile in find_intersection() ändern:

Die Feedback-Schleife ist kritisch:

Schnelle Tests → Oft ausführen → Bugs sofort fangen → Schnell reparieren
Langsame Tests → Selten ausführen → Bugs häufen sich → Schwer zu debuggen

4.3 Das Anti-Pattern: Der Test-Kegel (Umgekehrte Pyramide)

Was passiert, wenn Entwickler Unit-Tests vermeiden:

 /--------------\
  \   E2E     /    ← Viele E2E-Tests (langsam!)
   \----------/
    \ Module /      ← Einige Modul-Tests
     \------/
      \ Unit/       ← Wenige oder keine Unit-Tests
       \  /

Das ist rückwärts! Probleme:

  1. Langsames Feedback: Jede Änderung erfordert langsame E2E-Tests
  2. Schwer zu debuggen: E2E-Test schlägt fehl → welches von 10 Modulen hat es verursacht?
  3. Spröde: UI-Änderungen brechen alle E2E-Tests
  4. Entwickler vermeiden Tests: Zu langsam → Bugs häufen sich

Warum entsteht der Kegel?

Heute werden wir das beheben indem wir:

  1. Unit-Testing richtig lernen
  2. LLMs nutzen, um mühsames Boilerplate zu handhaben
  3. Unit-Testing so einfach machen, dass Sie es bevorzugen

5. Teil 3: Unit-Testing Deep Dive

5.1 Was ist ein Unit-Test?

Definition: Ein Unit-Test überprüft, dass eine einzelne Einheit (Funktion, Methode, Klasse) korrekt isoliert funktioniert.

Beispiel:

# Zu testende Einheit: find_intersection()
def test_find_intersection_normal_angle():
    """Test that find_intersection() works with a normal downward angle."""
    # Arrange: Testdaten aufsetzen
    x_road = np.array([0, 10, 20, 30])
    y_road = np.array([0, 2, 3, 4])
    angle = -10.0

    # Act: Funktion aufrufen
    x, y, dist = find_intersection(x_road, y_road, angle)

    # Assert: Ergebnis überprüfen
    assert x is not None, "Should find an intersection"
    assert dist > 0, "Distance should be positive"

Schlüssel-Charakteristiken:

5.2 Das AAA-Muster: Arrange-Act-Assert

Jeder gute Unit-Test folgt dieser Struktur:

def test_example():
    # ARRANGE: Testdaten und Bedingungen aufsetzen
    input_data = prepare_test_data()
    expected_result = calculate_expected()

    # ACT: Die zu testende Funktion aufrufen
    actual_result = function_under_test(input_data)

    # ASSERT: Überprüfen, dass das Ergebnis den Erwartungen entspricht
    assert actual_result == expected_result

Warum dieses Muster?

5.3 Die Unmöglichkeit des erschöpfenden Testens

Frage: Warum können wir nicht einfach jede mögliche Eingabe testen?

Machen wir die Mathematik für find_intersection():

def find_intersection(x_road, y_road, angle_degrees, camera_x=0, camera_y=1.5):
    """Find intersection between camera ray and road profile."""
    # ...

Nur ein Parameter: angle_degrees (ein Float)

Zeit pro Test: ~1 Millisekunde (sehr schnell!)

Gesamtzeit um nur angle_degrees zu testen:

1.000.000 Tests × 0.001 Sekunden = 1.000 Sekunden ≈ 17 Minuten

Aber warte, wir haben 4 Parameter:

Wenn wir ALLE Kombinationen testen (nur für 3 Float-Parameter):

1.000.000³ = 1.000.000.000.000.000.000 Tests (1 Quintillion!)

Bei 1ms pro Test:
= 1.000.000.000.000.000 Sekunden
= 31.709.791.983 Jahre
≈ 32 MILLIARDEN JAHRE

Zur Perspektive:

Und das ist für EINE Funktion mit einfachen Parametern!


5.4 Testing-Frameworks: Warum wir pytest brauchen

Frage: Können wir nicht einfach Tests als reguläre Python-Funktionen schreiben?

Ja, aber es wird schnell unübersichtlich:

# Ohne Testing-Framework:
def manual_test():
    x, y, dist = find_intersection(x_road, y_road, -10.0)
    if x is None:
        print("❌ FEHLGESCHLAGEN: x sollte nicht None sein")
        return False
    if dist <= 0:
        print("❌ FEHLGESCHLAGEN: Distanz sollte positiv sein")
        return False
    print("✅ BESTANDEN")
    return True

# Test ausführen
manual_test()

Probleme mit manuellem Testen:

  1. ❌ Keine automatische Test-Erkennung (Sie müssen jede Funktion manuell aufrufen)
  2. ❌ Keine organisierten Test-Reports (nur Prints)
  3. ❌ Keine Test-Isolation (ein Fehler könnte andere beeinflussen)
  4. ❌ Keine hilfreichen Fehlermeldungen (Sie müssen alle Checks schreiben)
  5. ❌ Keine Test-Fixtures (Setup/Teardown)
  6. ❌ Keine parallele Ausführung (langsam!)
  7. ❌ Keine Integration mit CI/CD

Hier kommt pytest: Das Industrie-Standard-Testing-Framework

Was pytest bietet:

# Automatische Test-Erkennung - findet alle test_*.py-Dateien
$ uv run pytest

# Mit detaillierter Ausgabe ausführen
$ uv run pytest -v

# Spezifische Test-Datei ausführen
$ uv run pytest tests/test_geometry.py

# Tests ausführen, die ein Muster matchen
$ uv run pytest -k "intersection"

# Print-Statements auch bei Bestehen zeigen
$ uv run pytest -s

# Bei erstem Fehler stoppen
$ uv run pytest -x

Was pytest großartig macht:

  1. Einfache Syntax: Nutze einfach assert (keine speziellen Methoden)
  2. Auto-Discovery: Findet automatisch alle test_*.py-Dateien
  3. Reiche Ausgabe: Zeigt genau was fehlgeschlagen ist und warum
  4. Fixtures: Teile Setup-Code zwischen Tests
  5. Parametrisierung: Führe denselben Test mit verschiedenen Eingaben aus
  6. Plugins: Erweitere Funktionalität (Coverage, Benchmarking, etc.)
  7. CI/CD-Integration: Funktioniert nahtlos mit GitHub Actions

Vergleich:

# Manuelles Testen (primitiv)
def test_something():
    result = function()
    if result != expected:
        print("FEHLGESCHLAGEN")
        return False
    return True

# pytest (modern)
def test_something():
    result = function()
    assert result == expected  # pytest kümmert sich um den Rest!

pytest-Ausgabe-Beispiel:

$ uv run pytest tests/test_geometry.py -v

============================= test session starts ==============================
platform win32 -- Python 3.12.0, pytest-8.0.0, pluggy-1.4.0
cachedir: .pytest_cache
rootdir: C:\...\road-profile-viewer
collected 5 items

tests/test_geometry.py::test_normal_angle PASSED                        [ 20%]
tests/test_geometry.py::test_vertical_angle PASSED                      [ 40%]
tests/test_geometry.py::test_empty_array FAILED                         [ 60%]
tests/test_geometry.py::test_boundary_angle PASSED                      [ 80%]
tests/test_geometry.py::test_upward_angle PASSED                        [100%]

=================================== FAILURES ===================================
________________________________ test_empty_array ______________________________

    def test_empty_array():
        x_road = np.array([])
        y_road = np.array([])
>       x, y, dist = find_intersection(x_road, y_road, -10.0)
E       IndexError: index 0 is out of bounds for axis 0 with size 0

tests/test_geometry.py:42: IndexError
========================= 1 failed, 4 passed in 0.12s =========================

Beachte:

Warum das wichtig ist:

Ohne pytest: "Etwas ist kaputt. Viel Glück beim Finden!"
Mit pytest:  "test_empty_array fehlgeschlagen in Zeile 42 mit IndexError"

Installation (bereits erledigt, wenn Sie uv haben):

# pytest ist typischerweise in Ihrer pyproject.toml aufgelistet
$ uv add --dev pytest

# Tests ausführen
$ uv run pytest

Jetzt, wo wir verstehen WARUM pytest existiert, lassen Sie uns unseren ersten Test schreiben!


5.5 Hands-On: Ihren ersten Unit-Test schreiben

Ziel: Teste die find_intersection()-Funktion.

Schritt 1: Test-Dateistruktur erstellen

# In Ihrem road-profile-viewer-Verzeichnis
$ mkdir tests
$ touch tests/__init__.py
$ touch tests/test_geometry.py

Schritt 2: Einen einfachen Test schreiben

# tests/test_geometry.py
import numpy as np
from numpy.typing import NDArray
import pytest
from road_profile_viewer.geometry import find_intersection


def test_find_intersection_finds_intersection_for_normal_angle() -> None:
    """
    Test that find_intersection() returns a valid intersection
    for a normal downward angle with a simple road profile.

    Equivalence class: Normal downward angles (-90° < angle < 0°)
    """
    # Arrange: Einfache aufwärts gehende Straße erstellen
    x_road: NDArray[np.float64] = np.array([0, 10, 20, 30], dtype=np.float64)
    y_road: NDArray[np.float64] = np.array([0, 2, 4, 6], dtype=np.float64)
    angle: float = -10.0  # Abwärtswinkel
    camera_x: float = 0.0
    camera_y: float = 10.0  # Kamera über Straße

    # Act: Schnittpunkt finden
    x, y, dist = find_intersection(
        x_road, y_road, angle, camera_x, camera_y
    )

    # Assert: Sollte einen Schnittpunkt finden
    assert x is not None, "x coordinate should not be None"
    assert y is not None, "y coordinate should not be None"
    assert dist is not None, "distance should not be None"
    assert dist > 0, "distance should be positive"
    assert 0 <= x <= 30, "intersection x should be within road bounds"

Schritt 3: Den Test ausführen

$ uv run pytest tests/test_geometry.py -v
============================= test session starts ==============================
collected 1 item

tests/test_geometry.py::test_find_intersection_finds_intersection_for_normal_angle PASSED [100%]

============================== 1 passed in 0.05s ===============================

Ihr erster Unit-Test besteht!


5.6 Clean Code Principles für Testing

Bevor wir tiefer in Äquivalenzklassen eintauchen, lassen Sie uns einige Clean-Code-Prinzipien für das Schreiben wartbarer Tests etablieren. Diese Prinzipien stammen aus Robert C. Martins “Clean Code” und sind essentiell, um Ihre Test-Suite lesbar und wartbar zu halten.


5.6.1 Ein Konzept pro Test

Prinzip: Jeder Test sollte EIN Konzept oder Verhalten überprüfen, nicht mehrere unabhängige Dinge.

Warum? Wenn ein Test fehlschlägt, wollen Sie sofort wissen, was kaputt ist. Tests mit mehreren Konzepten machen das Debugging schwerer.

❌ Schlechtes Beispiel: Mehrere Konzepte testen

def test_find_intersection_everything():
    """Dieser Test versucht zu viele Dinge auf einmal zu überprüfen."""
    # Test Abwärtswinkel
    x_road = np.array([0, 10, 20, 30], dtype=np.float64)
    y_road = np.array([0, 2, 4, 6], dtype=np.float64)
    x, y, dist = find_intersection(x_road, y_road, -10.0, 0.0, 10.0)
    assert x is not None  # Konzept 1: Abwärtswinkel findet Schnittpunkt

    # Test vertikaler Winkel
    x2, y2, dist2 = find_intersection(x_road, y_road, 90.0)
    assert x2 is None  # Konzept 2: Vertikaler Winkel gibt None zurück

    # Test leere Arrays
    x3, y3, dist3 = find_intersection(np.array([]), np.array([]), -10.0)
    assert x3 is None  # Konzept 3: Leere Arrays geben None zurück

Problem: Wenn dieser Test fehlschlägt, welches Konzept ist kaputt? Sie müssen den gesamten Test lesen, um es herauszufinden.

✅ Gutes Beispiel: Ein Konzept pro Test

def test_find_intersection_returns_intersection_for_downward_angle():
    """Test dass Abwärtswinkel Schnittpunkte finden."""
    x_road = np.array([0, 10, 20, 30], dtype=np.float64)
    y_road = np.array([0, 2, 4, 6], dtype=np.float64)
    x, y, dist = find_intersection(x_road, y_road, -10.0, 0.0, 10.0)
    assert x is not None

def test_find_intersection_returns_none_for_vertical_angle():
    """Test dass vertikale Winkel None zurückgeben."""
    x_road = np.array([0, 10, 20, 30], dtype=np.float64)
    y_road = np.array([0, 2, 4, 6], dtype=np.float64)
    x, y, dist = find_intersection(x_road, y_road, 90.0)
    assert x is None

def test_find_intersection_returns_none_for_empty_arrays():
    """Test dass leere Arrays None zurückgeben."""
    x, y, dist = find_intersection(np.array([]), np.array([]), -10.0)
    assert x is None

Vorteile:


5.6.2 Ein Assert pro Test (mit pragmatischen Ausnahmen)

Prinzip: Idealerweise sollte jeder Test EIN Assert-Statement haben. Aber es gibt vernünftige Ausnahmen.

Wann EIN Assert ideal ist:

def test_find_intersection_returns_positive_distance():
    """Test dass Distanz immer positiv ist, wenn Schnittpunkt gefunden."""
    x_road = np.array([0, 10, 20], dtype=np.float64)
    y_road = np.array([0, 2, 4], dtype=np.float64)
    x, y, dist = find_intersection(x_road, y_road, -10.0, 0.0, 10.0)
    assert dist > 0  # Ein Konzept: Distanz ist positiv

Wann MEHRERE Asserts akzeptabel sind:

Mehrere Asserts sind okay, wenn sie verschiedene Aspekte desselben Konzepts testen:

def test_find_intersection_returns_valid_coordinates():
    """Test dass zurückgegebene Koordinaten innerhalb erwarteter Grenzen liegen (gleiches Konzept)."""
    x_road = np.array([0, 10, 20, 30], dtype=np.float64)
    y_road = np.array([0, 2, 4, 6], dtype=np.float64)
    x, y, dist = find_intersection(x_road, y_road, -10.0, 0.0, 10.0)

    # Alle diese Asserts überprüfen das GLEICHE Konzept: gültige Koordinatengrenzen
    assert x is not None, "x sollte nicht None sein"
    assert y is not None, "y sollte nicht None sein"
    assert 0 <= x <= 30, f"x sollte in [0, 30] sein, bekam {x}"
    assert 0 <= y <= 6, f"y sollte in [0, 6] sein, bekam {y}"

Das ist akzeptabel, weil:

Wann man in mehrere Tests AUFTEILEN sollte:

Wenn Asserts verschiedene Konzepte testen, teile sie auf:

# ❌ Schlecht: Zwei verschiedene Konzepte in einem Test
def test_find_intersection_coordinates_and_distance():
    x_road = np.array([0, 10, 20], dtype=np.float64)
    y_road = np.array([0, 2, 4], dtype=np.float64)
    x, y, dist = find_intersection(x_road, y_road, -10.0, 0.0, 10.0)
    assert x > 0  # Konzept 1: x ist positiv
    assert dist > 0  # Konzept 2: Distanz ist positiv (anderes Konzept!)

# ✅ Gut: In zwei Tests aufgeteilt
def test_find_intersection_returns_positive_x():
    x_road = np.array([0, 10, 20], dtype=np.float64)
    y_road = np.array([0, 2, 4], dtype=np.float64)
    x, y, dist = find_intersection(x_road, y_road, -10.0, 0.0, 10.0)
    assert x > 0

def test_find_intersection_returns_positive_distance():
    x_road = np.array([0, 10, 20], dtype=np.float64)
    y_road = np.array([0, 2, 4], dtype=np.float64)
    x, y, dist = find_intersection(x_road, y_road, -10.0, 0.0, 10.0)
    assert dist > 0

5.6.3 Beschreibende Testnamen

Prinzip: Testnamen sollten beschreiben was getestet wird, welches Szenario, und was das erwartete Ergebnis ist.

Muster: test_[funktion]_[szenario]_[erwartetes_verhalten]()

❌ Schlechte Testnamen:

def test_1():  # Was testet das?
    pass

def test_intersection():  # Zu vage
    pass

def test_angle():  # Welcher Winkel? Was ist damit?
    pass

def test_edge_case():  # Welcher Randfall?
    pass

✅ Gute Testnamen:

def test_find_intersection_returns_none_for_empty_arrays():
    """Klar: Funktion + Szenario + erwartetes Ergebnis"""
    pass

def test_find_intersection_finds_intersection_for_downward_angle():
    """Sie wissen genau was das testet"""
    pass

def test_find_intersection_returns_none_when_ray_misses_road():
    """Szenario und Ergebnis sind explizit"""
    pass

def test_find_intersection_handles_vertical_angle_gracefully():
    """Klare Randfall-Behandlung"""
    pass

Warum das wichtig ist:

Testausgabe bei Fehler:

❌ FAILED test_1
   # Was sagt Ihnen das? Nichts!

✅ FAILED test_find_intersection_returns_none_for_empty_arrays
   # Sofort klar: Behandlung leerer Arrays ist kaputt

Real-World-Beispiel von find_intersection:

class TestFindIntersection:
    """Test-Suite für find_intersection() Funktion."""

    def test_find_intersection_returns_intersection_for_downward_angle(self):
        """Test normaler Abwärtswinkel findet Schnittpunkt."""
        pass

    def test_find_intersection_returns_none_for_vertical_angle(self):
        """Test vertikaler Winkel (90°) gibt None zurück."""
        pass

    def test_find_intersection_returns_none_for_empty_road_arrays(self):
        """Test leere Arrays geben None elegant zurück."""
        pass

    def test_find_intersection_returns_none_for_single_point_road(self):
        """Test Ein-Punkt-Straße (keine Segmente) gibt None zurück."""
        pass

    def test_find_intersection_finds_intersection_on_first_segment(self):
        """Test Schnittpunkterkennung auf erstem Straßensegment."""
        pass

Wenn Sie diese Tests ausführen:

$ uv run pytest tests/test_geometry.py -v
tests/test_geometry.py::TestFindIntersection::test_find_intersection_returns_intersection_for_downward_angle PASSED
tests/test_geometry.py::TestFindIntersection::test_find_intersection_returns_none_for_vertical_angle PASSED
tests/test_geometry.py::TestFindIntersection::test_find_intersection_returns_none_for_empty_road_arrays FAILED

Sie wissen sofort: “Leere-Array-Behandlung ist kaputt” ohne irgendeinen Code zu lesen!


5.6.4 Zusammenfassung der Clean Code Principles für Testing

Prinzip Richtlinie Warum es wichtig ist
Ein Konzept pro Test Jeder Test überprüft EIN Verhalten/Konzept Wenn Test fehlschlägt, wissen Sie sofort was kaputt ist
Ein Assert pro Test Idealerweise ein Assert; mehrere okay wenn gleiches Konzept getestet wird Fehler sind eindeutig; klar was schief ging
Beschreibende Namen test_[funktion]_[szenario]_[erwartet]() Testausgabe ist selbstdokumentierend; kein Code lesen nötig
AAA-Muster Arrange-Act-Assert (aus Abschnitt 5.2) Tests sind lesbar und konsistent

Diese Prinzipien arbeiten zusammen:

# ✅ Perfekter Test: Ein Konzept, ein Assert, beschreibender Name, AAA-Muster
def test_find_intersection_returns_positive_distance_for_valid_intersection():
    """Test dass Distanz von Kamera zu Schnittpunkt immer positiv ist."""
    # Arrange
    x_road = np.array([0, 10, 20, 30], dtype=np.float64)
    y_road = np.array([0, 2, 4, 6], dtype=np.float64)

    # Act
    x, y, dist = find_intersection(x_road, y_road, -10.0, 0.0, 10.0)

    # Assert
    assert dist > 0, f"Erwartete positive Distanz, bekam {dist}"

Vorteile dieser Prinzipien:

  1. Wartbarkeit: Einfach Tests zu aktualisieren, wenn Code sich ändert
  2. Debugging: Fehler zeigen sofort auf das Problem
  3. Dokumentation: Testnamen erklären, was der Code tun sollte
  4. Vertrauen: Klare Tests geben Ihnen Vertrauen zum Refactoring

6. Wie geht es weiter?

Wir haben die Grundlagen behandelt:

Aber es gibt eine kritische Frage, die wir noch nicht beantwortet haben: Wie entscheiden Sie, WELCHE Testfälle Sie schreiben sollen?

Sie können nicht alles erschöpfend testen. Wie wählen Sie also klug aus?

Weiter zu Kapitel 03 (Äquivalenzklassen): Äquivalenzklassen um fortgeschrittene Testdesign-Techniken zu lernen.

© 2026 Dominik Mueller   •  Powered by Soopr   •  Theme  Moonwalk