Direkt zum Hauptbereich

Test Driven Development 1

Ich arbeite gerade einen Vortrag über TDD aus und möchte hier schon einmal eine erste Version zur Kritik stellen.

Vorbemerkung

Ich spreche hier von einem Werkzeug, nicht von einem Entwicklungsdogma. Dieses Werkzeug hat Vor- und Nachteile und ist in machen Situationen gut zu gebrauchen und in anderen weniger. Mir geht es darum, dieses Werkzeug vorzustellen und seine Anwendung zu zeigen, damit daraufhin dann jeder Entwickler eine bewusste Entscheidung für oder gegen diese Technologie treffen kann.

Test First

Was wohl jeder Entwickler über Test Driven Development weiß, ist dass der Test bereits geschrieben und ausgeführt wird, bevor der zugehörige Produktivcode existiert.

Man schreibt zuerst einen Test, der fehlschlägt, dann schreibt man soviel Produktivcode wie nötig ist, damit der Test grün wird. Dann refaktorisiert man den Code und beginnt mit dem Zyklus erneut.

Entsprechend nennt man diesen Prozess: Red (Test schlägt fehl), Green (Test ist erfolgreich), Refactoring (Programmcode wird umstrukturiert).



Schauen wir uns diese Phasen im Detail an.

Red

Geht man vom klassischen Entwicklungmodell aus, mag es nach Zeitverschwendung aussehen, einen Test zu schreiben, in dem Wissen, dass dieser fehlschlagen wird. Doch dieser Schritt ist wichtig, weil wir damit den Test selbst testen. Wie prüfen damit, ob der Test fähig ist, einen Fehler - z.B. das Nichtvorhandensein einer Funktionalität - zu detektieren, wir also keinen tautologischen Test schreiben. Beim klassischen Vorgehen kann es passieren, dass wir den Test an die bereits umgesetzt Lösung anpassen, so dass der Test dann sofort grün ist. Eigentlich müssten wir dann den Entwickler bitten, bitte einmal einen Fehler einzubauen, um unseren Test zu prüfen. Das machen wir aber nicht. Ich habe selbst schon auf diese Weise tautologischen Tests geschrieben, sei es, weil ich eine Überprüfung vergessen habe, diese vorerst auskommentierte, aber dann vergaß wieder zu aktivieren oder weil ich, wider besseren Wissens das fertige Produkt als Referenz nahm und somit das fehlerhafte Verhalten zum Soll erklärte.

Deswegen bestehe ich inzwischen bei Bugfixes ziemlich intolerant immer auf eine genaue "how to reproduce" Anleitung bzw. möchte den Test immer noch einmal mit der fehlerhaften Version durchführen.

Green

Schlägt der Test fehl, soll soviel Produktivcode geschrieben werden, bis der Test grün wird. Und das soll schnell geschehen. Also nicht: "dann ziehe ich mich Mal zwei Monate zurück und melde mich, wenn ich fertig bin."
Ein Test soll nur kurz - wenige Minuten - rot sein, dann soll der Produktivcode den Test erfüllen.
Das ist der Punkt, an dem dann viele aussteigen und das Konzept für gescheitert betrachten. Die übrig gebliebenen ignorieren diese Regel und implementieren halt einfach länger an einer den Test erfüllenden Lösung.

Beide Wege basieren auf einem Missverständnis über die Bedeutung der Phase Green. Um dieses Missverständnis aufzuklären möchte ich zwei Möglichkeiten vorstellen, wie ein Test schnell grün geschaltet werden kann.

Die erste Möglichkeit ist logisch: Wenn es sich um ein sehr einfaches Problem handelt, welches durch einen trivialen Algorithmus komplett gelöst werden kann, können wir diesen Algorithmus sofort implementieren. Das dürfte in der Praxis eher selten der Fall sein.

Die zweite Möglichkeit hört auf den aussagekräftigen Namen "Fake it until you make it" und bedeutet letztlich nichts anderes, als dass wir einfach das gewünschte Ergebnis direkt als Konstante, ohne jegliche Logik in den Produktivcode einbetten. (ToDo: Hier Beispiele zeigen: Textkonvertierung, Codierung, Codegenerierung)

Dies mag einige Leser jetzt vielleicht etwas fassungslos staunen lassen; und auch hier wäre das oben bereits angesprochene Missverständnis die Ursache. Das Missverständnis nämlich, das in der Green Phase Produktimplementierung im Vordergrund stünde. In Wahrheit testen wir aber auch in dieser Phase unseren Test. Diesmal, ob er die Fähigkeit besitzt, eine korrekte Implementierung zu erkennen. Denn auch das kann uns passieren: Dass wir einen Test haben, der fehlschlägt, obwohl das Produkt korrekt ist. Das Resultat davon ist dann, dass wir tagelang nach einem Fehler im Produktivcode suchen, obwohl wir eigentlich im Testcode schauen müssen. Wenn unsere Produktivcode jetzt aber so simpel ist, dass sich darin kaum ein Fehler verstecken kann, wissen wir, dass wir unseren Test untersuchen müssen.

Am Ende von Phase Green haben wir für unseren Produktivcode noch nicht viel erreicht. Aber wir haben uns jetzt ein robustes Sicherheitsnetz aufgespannt, von dem wir wissen, dass es uns meldet, wenn wir von der idealen Lösung abweichen und und uns auch mitteilt, wenn wir (wieder) auf dem rechten Weg sind.

Refactoring

Die eigentliche Implementierungsarbeit erfolgt in der Phase, die leider oft übergangen wird: Beim Refactoring. Diese Phase ist also nicht nur ein unwichtiges Anhängsel, um den Code etwas aufzuhübschen. Beim Refactoring geht es darum, die eigentliche (saubere) Lösung aus dem schnellen und unsauberen oder eben gar gefakten Code herauszuarbeiten.

Die Phasen Red und Green haben hauptsächlich die Aufgabe uns einen soliden Test zu liefern, der uns dann in der Refactoring Phase die Sicherheit liefert, uns ganz auf das saubere Programmieren zu konzentrieren, ohne uns ständig mit der Frage beschäftigen zu müssen, ob unser Code denn noch korrekt ist. Das sagt uns die Maschine. Und zwar am besten bei jedem Tastendruck, ähnlich wie moderne Entwicklungsumgebungen uns schon beim Tippen auf Kompilierfehler hinweisen.

Das erfordert natürlich, dass die Test schnell durchgeführt werden.

Ich möchte diesen Punkt ganz besonders hervorheben: Refactoring ist keine zusätzlich einzuplanen Task. Kein Extra, dem man sich Mal widmet, wenn keine "richtige" Programmierarbeiten anliegen. Refactoring ist ein integraler Bestandteil jeder Entwicklung, sei es Featureentwicklung oder Bugfixing.

Wird fortgesetzt...

Kommentare

  1. In der Phase RED geht es nicht nur darum, einen aussagekräftigen Test zu schreiben. Sie dient auch dazu, die eigentliche Aufgabe bzw. Problemstellung zu verstehen. Was muss ich eigentlich lösen? Was sind die Grenzfälle? Wie sähe ein korrektes Ergebnis aus?

    RED ersetzt also zu einem gewissen Grad das Requirements Engineering oder zumindest ist es für den Entwickler ein Druckmittel, sich mit den Requirements auseinander zu setzen.

    Tatsächlich habe ich bis heute aber kein reines TDD Projekt erlebt. Ich meine, im echten Leben und nicht nur an irgendwelchen Beispielen. Speziell stören tut mich an TDD, dass man bei einem erfahrenen Entwickler erwarten kann, dass er mitdenkt und sich nicht in kleinsten Iterationen dem Problem nähert.

    Kulturell muss man wissen, dass TDD eine amerikanische Technik ist und damit in der Tradition des Rapid Prototyping ("fail fast") steht. In DE neigen wir hingegen mehr zur Ingenieurkunst, also dem Durchdenken einer Lösung, bevor wir sie realisieren. Ein wirklich guter Ansatz liegt meiner Meinung nach irgendwo in der Mitte.

    AntwortenLöschen
  2. Du hast Recht, dass das urpsrüngliche, von Kent Beck formulierte TDD so aussieht. Ich möchte in meinem Vortrag TDD ziemlich breite und tiefe behandeln. Dazu möchte ich auch die History darstellen, beginnend mit Becks Buch über die "TDD is dead"-Kontroverse bis zu Weiterentwicklungen und Varianten dieses Konzepts wie "TDD aiymi" oder "TDD 2.0" mit dem Schlagwort "Think first" und dem erweiterten Zyklus: Analyse -> Entwurf -> [Red -> Green -> Refactoring -> Red ->...].

    Ich bin mir halt noch nicht über den Aufbau des Vortrags schlüssig. Gehe ich chronologisch vor und beginne eben beim frühen TDD, erläutere, warum es nicht funktionierte, und stelle dann die Weiterentwicklungen vor. Oder überspringe ich diese "akademischen" Ausführungen und starte gleich bei den moderenen Varianten?

    Die Motivation muss ich auch noch irgendwo unterbringen. Und noch praktische Vorführungen...

    Deswegen dachte ich, ich nutze mal eben den Blog als Notizbuch und haue die verschiedenen Teile erst einmal raus und gucke dann, was ich ändere und in welche Reihenfolge bringe.

    AntwortenLöschen
  3. Wobei ich beim nochmaligen Lesen des Kommentars, einige Aussagen so nicht mehr stehen lassen würde. Es ist weniger ein Fehler in der ursprünglichen TDD-Konzeptions, als vielmehr Interpretations- bzw. Anwendungsfehler und die neueren Lösungen präzisieren eher schon vorhandene Punkte.

    Hach, es sind noch zu viele unstrukturierte Gedanke, die ich dazu habe.

    AntwortenLöschen
  4. Genau, du musst erst mal das Ziel des Vortrags definieren. Willst du eine praktische Einführung in TDD geben, würde ich die akademischen Diskussionen zur Historie weglassen. Dann zählt eher, wie das heutige Verständnis ist.

    AntwortenLöschen

Kommentar veröffentlichen

Beliebte Posts aus diesem Blog

Utopie gesucht

In den 90er Jahren gab es meiner Meinung nach eine positive Zukunftssicht. Das sah man u.a. in der Serie Star Trek The next generation. Heute dagegen scheint es nur noch pessimistische Blicke auf die Zukunft zu geben. Auch die aktuellen Star Trek Serien stellen eine düsterere Welt dar. Dies könnte zu einer selbst erfüllenden Prophezeiung werden. Gibt es in der aktuellen Popkultur noch Utopien?

I Want To Hold Your Hand

Ich habe gerade im Radio gehört, dass genau heute vor 60 Jahren die Beatles ihr Stück I Want To Hold Your Hand veröffentlicht haben. Dafür unterbreche ich gerne meine Blog-Abstinenz und zeige hier meine Lieblingsinterpretation dieses Lieds aus der wunderbaren Serie GLEE:

Ukulele

Das Wochende steht vor der Tür. Zeit mal wieder die Ukulele herauszuholen.