TDD – Wo liegt der Vorteil?
Neulich bin ich über einen interessanten Blogartikel gestolpert, wo es um Test Driven Development (TDD) ging: TDD in der (meiner) Praxis - Wunsch und Wirklichkeit. Test Driven Development bedeutet im Wesentlichen, dass ein automatisierter Test bereits geschrieben wird, bevor die Implementierung des zugehörigen Anwendungscodes stattfindet.
Kann ich mir TDD leisten?
Da ging es vor allem um die Frage: Kann ich mir TDD eigentlich leisten? Wie hoch ist der Mehraufwand? Und wenn ich das in mein Angebot einpreise, kriege ich dann überhaupt noch einen Auftrag?
Oder andersherum gefragt: Wieviel TDD kann ich mir leisten? Welche Funktionalitäten sind es wert getestet zu werden und wo kann ich gegebenenfalls auf Tests verzichten, um konkurrenzfähig zu bleiben?
Der Artikel ist auf jeden Fall lesenswert und das gilt auch für die nachfolgende sehr ausführliche Diskussion in Form von Kommentaren. Ich möchte hier einige eigene Gedanken dazu beisteuern, aber lassen Sie mich vorher noch einmal einen Schritt zurückgehen.
Was soll TDD denn eigentlich leisten?
Argumente für TDD gibt es viele. Hier eine kleine Auswahl der wichtigsten:
- weniger Bugs / bessere Qualität bei Auslieferung
- weniger Kosten, da Fehler früher gefunden werden
- weniger Angst vor Refactoring / Änderungen allgemein
- persönliche Befriedigung des Entwicklers
- besseres Design
- höhere Produktivität
Wenn ich mir diese Liste so anschaue stelle ich fest, dass die ersten 4 Punkte eher Argumente für automatisierte Tests im Allgemeinen aber nicht unbedingt für TDD im Speziellen sind.
Weniger Bugs bei Auslieferung (und damit gleichzeitig auch weniger Kosten und bessere Qualität) erhalte ich in jedem Fall. Da ist es relativ egal, ob ich die Tests vor oder nach der Implementierung schreibe.
Dasselbe gilt für die Angst vor dem Refactoring von kritischen Programmbestandteilen und die persönliche Befriedigung des Entwicklers über das Schreiben von funktionierendem Code. Sicher wird es leichte Unterschiede geben aber ich halte die für vernachlässigbar.
Eine Voraussetzung gibt es allerdings: Die neu entwickelten Funktionalitäten dürfen nicht zu umfangreich sein. Und außerdem muss es Tests für den bestehenden Code geben. Wenn ich also ein halbes Jahr entwickle und dann anschließend die Tests für meinen Code schreibe, dann wird das einen deutlichen Unterschied beim Aufwand im Vergleich zu TDD machen.
Solange wir aber über kleine Einheiten von wenigen Wochen reden (typisch beispielsweise für Sprints in der agilen Softwareentwicklung oder die Entwicklung einzelner Features), ist der Unterschied sicher vernachlässigbar. Wichtig dabei ist, dass der Testcode zu dieser Einheit dazugehört.
Und was ist mit dem Design?
Test Driven Development führt zu besserem Design – kriegt man jedenfalls oft zu hören. Ich glaube da muss man etwas differenzieren.
Auf der Komponentenebene, wo ich einzelne Methoden einer Klasse teste, macht es meiner Meinung nach keinen Unterschied. Warum?
Woran erkennt man gutes Design? Es lässt sich leicht erweitern, ist leicht zu verstehen, vermeidet viele Abhängigkeiten und nicht zuletzt bildet es die Realität möglichst naturgetreu ab.
Und wie kommt man zu einem solchen Design? Durch viel Wissen und noch mehr Erfahrung. Aber sicher nicht durch Unit Tests. Wenn Sie nicht wissen, wie man ein gutes Design entwirft und wodurch es sich auszeichnet, dann werden Unit Tests sie nicht „automagisch“ dort hinführen.
Etwas anders sieht das aus bei Tests, die dem normalen Arbeitsablauf der Benutzung einer Software nachempfunden sind. Das sind dann keine klassischen Unit Tests mehr aber natürlich kann man die auch automatisieren (siehe Artikel Unit-Tests – Zeitverschwendung?).
So ein Arbeitsablauf ist besonders wichtig in der Anforderungsklärung und das hat natürlich auch Auswirkungen auf das Design der Anwendung. Für mich wird aber eigentlich andersherum ein Schuh daraus: Erst ermittle ich die Arbeitsabläufe, dann generiere ich daraus die zugehörigen Testfälle.
Das führt dann auch zu einem besseren Design, aber nicht aufgrund von TDD. Es sei denn, ich habe mir vorher keine Gedanken über die Arbeitsabläufe gemacht, dann wird das bei der Definition der Testfälle sozusagen implizit nachgeholt.
Insofern kann ich sagen, dass TDD hier tatsächlich zu einem besseren Design führt. Wenn ich allerdings vorher meine Hausaufgaben gemacht habe, dann brauche ich dafür TDD nicht mehr – jedenfalls nicht für das Design.
Manche Entwickler sind ja der Meinung, dass Testfälle allein als Ersatz für eine Spezifikation dienen können. Ich habe mich für diesen Gedanken nie so recht erwärmen können. Was denken Sie darüber?
Produktivität
Damit bin ich beim letzten Punkt angelangt: Ist ein Entwickler mit TDD produktiver als ohne? Die Frage finde ich gar nicht so leicht zu beantworten. Ich denke, dass hier mehrere verschiedene Aspekte eine Rolle spielen.
- Die Art der Anwendung: Bei interaktiven Anwendungen wie beispielsweise Verwaltungssoftware auf Basis von Datenbanken, wo es hauptsächlich um die Anzeige und Pflege ausgewählter Informationen geht, ist der manuelle Testaufwand traditionell sehr groß. Hier erscheint mir TDD sehr attraktiv, da es den Zeitaufwand des Testens stark reduziert. Bei Batchanwendungen dagegen gewinnt man durch Automatisierung nicht viel. Die sind ja im Grunde von Haus aus schon automatisiert.
- Entwicklungsgewohnheiten: Wenn ich selber entwickle, schreibe ich normalerweise 10–20 Zeilen Code und teste diese dann. Ganz selten sind es einmal 50 Zeilen oder mehr. Das bedeutet, dass Tests einen beträchtlichen Teil meiner Entwicklungszeit ausmachen. Ich habe es nie wirklich gemessen, aber es würde mich nicht wundern wenn der Anteil bei 20–40 Prozent liegt. In so einem Fall können automatisierte Tests tatsächlich eine Menge Zeit sparen – mehr als es kostet sie zu schreiben.
- Testaufwand: Die meisten Tests kann man sehr schnell erstellen. Im Gegensatz zur Entwicklung der eigentlichen Anwendung muss hier nicht so viel Planungsaufwand betrieben werden. Tests können aber auch recht aufwändig werden, z.B. wenn Sie komplette Arbeitsabläufe bei einer Datenbankanwendung testen wollen. Wenn das wirklich sauber funktionieren soll, dann müssen die dazugehörigen Daten komplett für die Tests bereitgestellt werden – und anschließend wieder entsorgt werden, damit der nächste Testdurchlauf auch noch funktioniert.
Grundsätzlich habe ich die Erfahrung gemacht, dass TDD sich lohnt – auf jeden Fall bei den Anwendungen, mit denen ich hauptsächlich zu tun hatte. Unter anderen Rahmenbedingungen kann es aber auch vorkommen, dass Sie keinen Nutzen daraus ziehen.
Wohlgemerkt: Ich beziehe das nur auf TDD im Speziellen, nicht auf automatisierte Tests im Allgemeinen.
TDD und Termine
Es gibt noch einen weiteren wichtigen Aspekt im Zusammenhang mit TDD: Es sorgt dafür, dass automatisierte Tests auch tatsächlich geschrieben werden!
Das hört sich auf den ersten Blick ein wenig seltsam an. Falls Wenn ihr Projekt jedoch unter Termindruck gerät, dann gehören Tests zu den Dingen, die als erstes unter den Tisch fallen (das gilt übrigens nicht nur für automatisierte Tests).
Mit TDD kann das nicht passieren, denn die Tests werden noch vor dem eigentlichen Code geschrieben. Das führt dazu, dass die Qualität der Software bestehen bleibt und dann eher der Terminplan oder der Funktionsumfang angepasst werden – es sei denn die Tests werden komplett gestrichen.
Dieser Punkt allein wäre für mich schon ausreichend, um TDD einzuführen – natürlich nur, sofern die übrigen Rahmenbedingungen für automatisierte Tests sprechen.
Passendes Spiel-Kapitel: Testing
Was passiert, wenn unter Termindruck die Tests wegrationaliert werden, können Sie im Kapitel „Testing“ des PM-Spiels am eigenen Projekt erleben – inklusive der Abbaudauer eines gewachsenen Fehlerbergs.