Ein spannendes Beispiel über den Einsatz von TDD-Praktiken liefert Jason Gorman, der an sechs Tagen eine Konvertierung von Dezimalzahlen in römische Zahlen implementiert hat. Am ersten, dritten und fünften Tag hat er TDD-Praktiken eingesetzt, an den anderen Tagen nicht. Zwischen den Implementierungen lagen immer mehrere Tage Pause, um genügend Abstand zu gewinnen. In jedem Durchlauf hat er die Zeit gemessen, die er brauchte, bis er ein festgesetztes Set an Testfällen implementiert hatte. In allen Durchläufen mit TDD-Praktiken war er schneller, als an Tagen ohne.

Ich habe diese Geschichte einem Kollegen erzählt, der vom Einsatz von Tests generell noch nicht überzeugt ist. Wie erwartet hat er das Ergebnis angezweifelt und konnte sich diesen Effekt auf die Umsetzungsgeschwindigkeit nicht erklären. Er hat den Test in den nächsten Tagen selbst begonnen, ist aber umgekehrt vorgegangen. Am ersten Tag hat er die Umwandlung ohne TDD-Einsatz implementiert, am zweiten Tag mit Tests. Am zweiten Tag war er tatsächlich etwas langsamer als am ersten Tag. Das Lustige an der Sache: Am zweiten Tag hat er festgestellt, dass seine Implementierung vom ersten Tag fehlerhaft war.

Zwei Fragen bleiben offen:

  1. Wo genau liegt der Unterschied zwischen Test Driven Development und dem Einsatz von Tests?
  2. Wieso sollte man mit dem Einsatz von TDD schneller Software entwickeln können?

Tests

Der Einsatz von Tests sollte in der Softwareentwicklung selbstverständlich sein. Ohne in Diskussionen über nötige Testabdeckungen, Unit-Tests und Integrationstests abschweifen zu wollen: Am Einsatz von automatisierten Tests führt kein Weg vorbei.

Test-First

Im Extreme Programming gilt der Test-First-Ansatz. Bevor Logik implementiert wird, erfolgt zunächst ein Test, der die zu implementierende Logik testet.

Test Driven Development

TDD – Test Driven Development – nach Kent Beck geht aber über die reine Arbeit von Tests im Sinne der Qualitätssicherung hinaus. TDD ist ein Entwicklungs- und Designparadigma.

Hier legt man über die Implementierung von Tests in kleinen Schritten das Design und die Architektur der Programmlogik fest. Der Test wird immer so lange geschrieben, bis er ausführbar ist und fehlschlägt. Die Programmlogik wird dabei so lange implementiert, bis der Test ohne Fehler ausgeführt werden kann. Nach diesen beiden Schritten können in einem Refactoring-Schritt Duplikate aufgelöst werden. Dieses Vorgehen nennt man auch Red-Green-Refactor. Welche Tests geschrieben werden, beschreibt die Testliste, die man zu Beginn des Projektes oder der Umsetzungsphase erstellt.

Was macht TDD aus?

Fake it ‘til you make it. Im “Green”-Schritt wird eine Fake-Implementierung vorgenommen, bis die korrekte Abstrahierung klar ersichtlich ist. Man gibt feste Werte zurück oder lässt Duplikate entstehen statt Schleifen direkt zu implementieren.

Umsetzungsgeschwindigkeit

Die Frage ist immer noch: Warum sollte man (funktionierende) Software auf diesem Weg schneller entwickeln können?

Divide & Conquer

Ein Teil, der die Umsetzungsgeschwindigkeit erhöht, ist die Trennung von Funktionalität und Qualität. Man kümmert sich erst rein um das Bestehen des aktuellen Testfalls (green), bevor man sich getrennt davon mit der Code-Qualität beschäftigt (refactor). Die Aufteilung der beiden Tätigkeiten vereinfacht Entscheidungen, die wir Entwickler beim Programmieren permanent treffen müssen.

Ein weiterer Baustein ist die Tatsache, dass man durch die Erstellung der Testliste sehr anforderungsbezogen entwickelt. Es darf nur implementiert werden, was durch einen fehlgeschlagenen Test gefordert ist. Die Versuchung, Zusatzfunktionalität einzufügen, die eventuell für zukünftige Erweiterungen gebraucht wird, wird so unterbunden. Der Entwickler erhält durch das Vorgehen einen klaren Fokus auf das aktuelle Projekt.

Den größten Einfluss hat aber das konsequente Herunterbrechen von Anforderungen bis in kleinste Bausteine. Wir teilen Projekte in einzelne User-Stories auf, aus denen kleinere Aufgaben entstehen. Jede Aufgabe wird dann in mehrere einzelne Commits aufgeteilt. TDD liefert den nächsten Schritt und teilt einzelne Commits in kleine Bausteine auf. Der Entwickler springt immer zwischen Test und Logik hin und her und erweitert beide Teile in den red/green-Schritten.

Programmierer treffen permanent Entscheidungen – über die Aufteilung des Programmcodes in Klassen und Methoden, über Namensgebungen und so weiter. Diese Entscheidungen werden durch das Herunterbrechen in kleinste Iterationen vereinfacht, wodurch die Umsetzungsgeschwindigkeit erhöht wird.

Testabdeckung

Dass man durch das TDD-Vorgehen eine Testabdeckung sehr, sehr nah an 100 % erhält, ist schon fast ein Nebeneffekt. Fehler fallen bereits sehr früh in kleinen Schritten auf und können daher sehr schnell behoben werden. Das zeitaufwändige Suchen der letzten Fehler gegen Ende eines Projektes oder der Implementierungsphase entfällt.

Fazit

Der Einsatz von Tests ist heute selbstverständlich. TDD-Praktiken bauen darauf auf und ermöglichen es, erfahrenen Entwicklern sehr schnell qualitativ guten, funktionierenden Code zu implementieren.

Wer in das Thema Test Driven Development einsteigen möchte, dem empfehle ich das Buch “Test Driven Development: By Example von Kent Beck” und den Blog von Ron Jeffries.