Test-Driven Development (TDD) in der Praxis
TDD wird oft als Dogma verkauft. Ich sehe es pragmatisch: als Technik, die in bestimmten Situationen exzellenten Code hervorbringt — und in anderen nur bremst. Hier zeige ich den Zyklus an einem echten Beispiel und ordne ehrlich ein, wann er sich lohnt.
Was TDD wirklich ist
Test-Driven Development heißt: Du schreibst den Test vor dem Code. Der Test definiert, was der Code tun soll — und treibt so das Design. Das klingt paradox, ergibt aber sauberere, besser testbare Schnittstellen.
Red-Green-Refactor
- Red: Schreibe einen Test für Verhalten, das noch nicht existiert. Er schlägt fehl.
- Green: Schreibe den minimalen Code, damit der Test grün wird. Nicht mehr.
- Refactor: Verbessere den Code, während die Tests grün bleiben.
Die Disziplin liegt im "minimalen Code". Nicht vorausprogrammieren, was man glaubt später zu brauchen — nur was der aktuelle Test verlangt.
Ein Beispiel von Anfang bis Ende
Aufgabe: eine Funktion, die eine IBAN grob validiert. Wir gehen den Zyklus durch.
Red — Test zuerst, Funktion existiert noch nicht:
# test_iban.py
from iban import ist_gueltige_iban
def test_leere_iban_ungueltig():
assert ist_gueltige_iban("") is False
# -> schlägt fehl: ImportError, iban.py existiert nichtGreen — minimaler Code:
# iban.py
def ist_gueltige_iban(iban: str) -> bool:
return bool(iban)
# -> Test grün. Mehr nicht.Red — nächster Fall treibt die Logik weiter:
def test_zu_kurz_ungueltig():
assert ist_gueltige_iban("DE12") is False
def test_korrekte_laenge_gueltig():
assert ist_gueltige_iban("DE89370400440532013000") is TrueGreen — Code wächst nur so weit wie nötig:
def ist_gueltige_iban(iban: str) -> bool:
iban = iban.replace(" ", "")
if not iban:
return False
# Deutsche IBAN: 22 Zeichen
if iban.startswith("DE") and len(iban) != 22:
return False
return TrueDer Refactor-Schritt
Sind mehrere Tests grün, verbessert man die Struktur — mit dem Sicherheitsnetz der Tests:
LAENGEN = {"DE": 22, "AT": 20, "CH": 21}
def ist_gueltige_iban(iban: str) -> bool:
iban = iban.replace(" ", "").upper()
if len(iban) < 4:
return False
land = iban[:2]
if land in LAENGEN and len(iban) != LAENGEN[land]:
return False
return True
# Alle Tests bleiben grün -> Refactoring war sicherWann TDD sich lohnt — und wann nicht
Lohnt sich:
- Klar umrissene Logik mit vielen Fällen (Validierung, Berechnungen, Parser)
- Bug-Fixes: erst ein Test, der den Bug reproduziert, dann der Fix
- Wenn die Schnittstelle noch unklar ist — der Test zwingt zu einem sauberen Design
Lohnt sich weniger:
- Explorativer Code / Prototypen, wo sich alles noch ständig ändert
- Reine Verdrahtung (ein View, der nur Daten durchreicht)
- UI-Layout und ähnlich schwer in Tests fassbare Dinge
Mein pragmatischer Ansatz: TDD für Kernlogik und Bug-Fixes, lockerer bei Verdrahtung und Prototypen. Dogma hilft niemandem — Wirkung schon.
Fazit
TDD ist kein Selbstzweck, sondern ein Werkzeug: Der Red-Green-Refactor-Zyklus erzeugt gut testbaren Code mit sauberen Schnittstellen und einem Sicherheitsnetz für Refactorings. Am stärksten ist er bei klar umrissener Logik und bei Bug-Fixes. Dort setze ich ihn konsequent ein — anderswo mit Augenmaß.
