Testgetriebene EntwicklungTestgetriebene Entwicklung (auch testgesteuerte Programmierung; englisch test first development oder test-driven development, TDD) ist eine Methode, die häufig bei der agilen Entwicklung von Computerprogrammen eingesetzt wird. Bei der testgetriebenen Entwicklung erstellt der Programmierer Softwaretests konsequent vor den zu testenden Komponenten. Gründe für die Einführung einer testgetriebenen EntwicklungNach klassischer Vorgehensweise, beispielsweise nach dem Wasserfall- oder dem V-Modell, werden Tests parallel zum und unabhängig vom zu testenden System entwickelt oder sogar nach ihm. Dies führt oft dazu, dass der Code schwer testbar ist und somit der Aufwand für die Tests hoch ist und diese nicht die gewünschte oder erforderliche Testabdeckung erzielen. Mögliche Gründe dafür sind unter anderem:
Die Methode der testgetriebenen Entwicklung versucht diesen Nachteilen der White-Box-Tests entgegenzuwirken und dabei auch ein auf die Aufgabenstellung der Software besser angepasstes und wartbareres Softwaredesign zu bekommen. Bei der Verwendung von testgetriebener Entwicklung können im Schnitt 45 Prozent aller Fehler erkannt bzw. vermieden werden.[1] Im Vergleich dazu werden beim reinen Einsatz von Unittests im Schnitt nur 30 Prozent der Fehler erkannt.[2] VorgehensweiseBei der testgetriebenen Entwicklung ist zwischen dem Testen im Kleinen Modultests (englisch unit tests) und dem Testen im Großen (Integrationstests, Systemtests, Akzeptanztests) zu unterscheiden, wobei Kent Becks Methode auf Unit-Tests ausgelegt ist. Testgetriebene Entwicklung mit Unit-Tests (englisch middle-out TDD)tests firstUnit-Tests werden vor dem eigentlichen Computerprogramm geschrieben. Es ist nicht festgelegt, ob der Programmierer, der die Implementierung vornehmen wird, auch die Unit-Tests erstellt. Es ist erlaubt, dass mehrere fehlschlagende Unit-Tests gleichzeitig existieren. Die Umsetzung des von einem Unit-Test geforderten Verhaltens im Computerprogramm kann zeitlich verschoben werden. Die Methode tests first kann als Vorstufe der testgetriebenen Entwicklung betrachtet werden. TDD nach Kent BeckUnit-Tests und mit ihnen getestete Units werden stets parallel entwickelt. Die eigentliche Programmierung erfolgt in kleinen, wiederholten Mikroiterationen. Eine solche Iteration, die nur wenige Minuten dauern sollte, hat drei Hauptteile, die man im englischen Red-Green-Refactor nennt
Diese drei Schritte werden so lange wiederholt, bis die bekannten Fehler bereinigt sind, der Code die gewünschte Funktionalität liefert und dem Entwickler keine sinnvollen weiteren Tests mehr einfallen, welche vielleicht noch scheitern könnten. Die so behandelte programmtechnische Einheit (Unit) wird dann als einstweilen fertig angesehen. Die gemeinsam mit ihr geschaffenen Tests werden beibehalten, damit auch nach künftigen Änderungen wiederum daraufhin getestet werden kann, ob die schon erreichten Aspekte des Verhaltens weiterhin erfüllt werden. Damit die – auch Transformationen genannten – Änderungen in Schritt 2 zum Ziel führen, muss jede Änderung zu einer allgemeineren Lösung führen; sie darf also nicht etwa nur den aktuellen Testfall auf Kosten anderer behandeln. Tests, die immer mehr ins Einzelne gehen, treiben den Code so zu einer immer allgemeineren Lösung. Die Beachtung der Transformationsprioritäten führt dabei regelmäßig zu effizienteren Algorithmen.[3] Die konsequente Befolgung dieser Vorgehensweise ist eine evolutionäre Entwurfsmethode, indem jede der einzelnen Änderungen das System weiterentwickelt. Testgetriebene Entwicklung mit System- oder Akzeptanztests (englisch outside-in TDD)Systemtests werden bei testgetriebener Entwicklung immer vor dem System selbst entwickelt oder doch wenigstens spezifiziert. Aufgabe der Systementwicklung ist bei testgetriebener Entwicklung nicht mehr, wie klassisch, schriftlich formulierte Anforderungen zu erfüllen, sondern spezifizierte Systemtests zu bestehen. Akzeptanztestgetriebene Entwicklung (ATDD) ist zwar mit testgetriebener Entwicklung verwandt, unterscheidet sich jedoch in der Vorgehensweise von testgetriebener Entwicklung.[4] Akzeptanztestgetriebene Entwicklung ist ein Kommunikationswerkzeug zwischen dem Kunden bzw. den Anwendern, den Entwicklern und den Testern, welches sicherstellen soll, dass die Anforderungen gut beschrieben sind. Akzeptanztestgetriebene Entwicklung verlangt keine Automatisierung der Testfälle, wenngleich diese fürs Regressionstesten hilfreich wäre. Die Tests bei akzeptanztestgetriebener Entwicklung müssen dafür auch für Nicht-Entwickler lesbar sein. Die Tests der testgetriebenen Entwicklung können in vielen Fällen aus den Tests der akzeptanztestgetriebenen Entwicklung abgeleitet werden. GemeinsamkeitenNeben anderen Zielen werden bei allen Arten von testgetriebener Entwicklung eine möglichst vollständige Testautomatisierung angestrebt. Für testgetriebene Entwicklung müssen alle Tests einfach („per Knopfdruck“) und möglichst schnell ausgeführt werden können. Für Unit-Tests bedeutet das eine Dauer von wenigen Sekunden, für Akzeptanz- oder Systemtests von maximal einigen Minuten, bzw. nur in Ausnahmen länger. Die großen Vorzüge der testgetriebenen Methodik gegenüber der klassischen sind:
EinsatzgebieteTestgetriebene Entwicklung ist wesentlicher Bestandteil des Extreme Programming (XP) und anderer agiler Methoden. Auch außerhalb dieser ist testgetriebene Entwicklung anzutreffen, häufig in Verbindung mit der Paarprogrammierung. Als Übungsmethode werden oft Katas eingesetzt. WerkzeugeDie testgetriebene Entwicklung braucht vordringlich
Bei der Java-Entwicklung kommen meist Ant, Maven oder Gradle und JUnit zum Einsatz. Für die meisten anderen Programmiersprachen existieren ähnliche Werkzeuge, wie z. B. für PHP PHPUnit oder für C Ceedling, Unity und CMock.[6] Für komplexe Systeme müssen mehrere Teilkomponenten unabhängig voneinander entwickelt werden und es finden dazu auch noch Fremdkomponenten Verwendung, etwa ein Datenbanksystem zwecks persistenter Datenhaltung. Die korrekte Zusammenarbeit und Funktion der Komponenten im System muss dann auch getestet werden. Um nun die Einzelkomponenten dabei separat testen zu können, die doch aber zu ihrer korrekten Funktion wesentlich von anderen Komponenten abhängen, verwendet man Mock-Objekte als deren Stellvertreter. Die Mock-Objekte ersetzen und simulieren im Test die benötigten anderen Komponenten in einer Weise, die der Tester ihnen einprogrammiert. Werkzeuge für Akzeptanztests und Systemtests sind beispielsweise Framework for Integrated Test oder Cucumber. Eine beliebte FIT-Variante ist Fitnesse, ein Wiki-Server mit integrierter Testerstellungs- und Testausführungsumgebung. KritikAufwand-Einwand und GegeneinwändeHaupteinwand gegen testgetriebene Entwicklung ist der vermeintlich hohe Aufwand. Die beschriebene Idee macht sich aber zunutze, dass der gedankliche Aufwand, der beim Programmieren in die konstruktive Beschreibung, also das Programm, investiert wird, und den Hauptteil der Programmierzeit ausmacht (im Verhältnis zum Tippaufwand etwa), eine Aufzählung einzelner zu erfüllender Punkte und Fälle beinhaltet. Mit nur wenig mehr Aufwand lässt sich also genau zu diesem Zeitpunkt vor der Programmierung der abzudeckende Fall beschreiben, das vorherige Schreiben weniger Testzeilen kann sogar zu einer besseren gedanklichen Strukturierung und höherer Codequalität führen. Zweitens führt die testgetriebene Entwicklung zu einer bestimmten Disziplin, welche Funktionen in welcher Reihenfolge implementiert werden, weil man sich erst einen Testfall überlegen muss, und damit potentiell zu einer höheren Berücksichtigung des Kundennutzens, siehe auch YAGNI. Unit-Tests oder automatisierte Tests allgemein werden oftmals als das Sicherheitsnetz eines Programms bei notwendigen Änderungen beschrieben, ohne eine hohe Testabdeckung ist ein Softwaresystem grundsätzlich anfälliger für Fehler und Probleme in der Weiterentwicklung und Wartung.[7] Schon bei der ersten Erstellung kann der Aufwand mit TDD bei ein wenig Übung zu der Erfüllung einer bestimmten Funktionalität also unter dem Aufwand einer vermeintlich schnellen Lösung ohne automatisierte Tests liegen. Dies gilt nach übereinstimmender Ansicht umso mehr, je langlebiger das System ist und damit wiederholt Änderungen unterliegt. Der Aufwand, automatisierte Tests nachträglich zu schreiben, ist wesentlich höher, weil gedanklich die einzelnen Anforderungen und Programmzeilen noch einmal analysiert werden müssen, und eine vergleichbare Testabdeckung wie bei TDD ist alleine aus Aufwands- und Kostengründen dann kaum noch realistisch. Konsequenz ist nötigAuch die Methode der testgetriebenen Entwicklung kann falsch eingesetzt werden und dann scheitern. Programmierern, die noch keine Erfahrung dabei besitzen, erscheint sie manchmal schwierig oder gar unmöglich. Sie fragen sich, wie man etwas testen soll, das doch noch gar nicht vorhanden ist. Auswirkung kann sein, dass sie die Prinzipien dieser Methode vernachlässigen, was insbesondere bei agilen Methoden wie dem Extreme Programming Schwierigkeiten beim Entwicklungsprozess oder sogar dessen Zusammenbruch zur Folge haben kann. Ohne ausreichende Unit-Tests wird keine ausreichende Testabdeckung für das Refactoring und die gewünschte Qualität erreicht. Dem kann man mit Paarprogrammierung und Schulung entgegenwirken. Ausbildung/Übung erforderlichEin wesentliches Argument von Gegnern der testgetriebenen Entwicklung ist, dass insbesondere Unit-Tests den Aufwand bei Änderungen an bestehender Funktionalität unnötig erhöhen, weil eine Änderung am Produktions-Code unverhältnismäßig viele Unit-Tests fehl schlagen lässt. Die Ursache dafür liegt jedoch in der Regel darin, dass die getestete Unit nicht ausreichend separiert wurde, die Tests also nicht atomar sind. Um dieses Problem zu vermeiden, ist es notwendig, dass die Programmierer darin geschult werden, wie sie die Anforderungen in atomare Funktionseinheiten zerlegen können und dies üben. Kein Ersatz für alle anderen TestartenAuch diese stark auf Tests setzende Art der Programmierung kann wie alle Testarten nicht jeden Fehler aufdecken:
Keine der genannten Testarten und Vorgehensweisen kann alle Fehler aufdecken, darum sollten in den meisten Fällen mehrere Testarten und fehlervermeidende Vorgehensweisen angewendet werden. Literatur
Weblinks
Einzelnachweise
|