Qualitätssicherung ausgewogen

Seite 3: TDD und ATDD

Inhaltsverzeichnis

Bei statischer Codeanalyse werden häufig Tools wie FindBugs, FXCop oder Checkstyle eingesetzt, um den Code gegen applikationsunabhängige Fehler zu prüfen. Nicht abgedeckte Exceptions, offensichtliche Null-Pointer-Referenzen und ungenutzte Code-Fragmente lassen sich so gezielt finden. In der Regel sollte man sich jedoch nicht allein auf die statischen Codeanalysen beschränken, sondern die Metriken durch Paarprogrammierung ergänzen. Dadurch wird auch langfristig der Wissensaustausch im Team gefördert und Collective Code Ownership ermöglicht. Wenn jeder Programmierer im Team in der Lage ist, jede andere Codestelle zu ändern, entsteht eine ganze neue Form der Zusammenarbeit, da auf einmal nicht mehr Peters Code nicht mehr geht, sondern es sich um den Code aller Teammitglieder handelt.

Wie bei allen Metriken ist jedoch im Auge zu behalten, dass es nicht darum geht, die individuelle Leistung von Teammitgliedern zu messen oder beispielsweise Coverage-Metriken als Zielvorgabe für das Softwareentwicklungsteam zu verwenden. Cem Kaner und Walter P. Bond führen in "Software Engineering Metrics - What do they measure and how do we know?" [3] an, dass ein solches Verhalten zum Verfälschen und Austricksen der Metriken führt. So lassen sich Unit-Tests gezielt so anlegen, dass die prozentuale Abdeckung erreicht wird. Sie sind dann meist stark an die Applikationslogik gebunden, sodass es im Nachhinein schwer ist, den Code zu ändern, ohne den Test zu brechen. Stattdessen schlagen die Autoren vor, in die Ausbildung der Entwickler zu investieren und direkt mit ihnen am Code zu arbeiten.

Wenn einmal ein Netz automatisierter Tests zusammen mit der kontinuierlichen Integration in Position gebracht ist, ist es nur noch ein kleiner Schritt zur testgetriebenen Entwicklung (TDD). Der Entwicklungszyklus beginnt dann mit einem kleinen, fehlschlagenden Test: Läuft ein Test nicht durch, darf gerade so viel Produktionscode geschrieben werden, dass er sich erfolgreich abschließen lässt. Anschließend sind Redundanzen im Code durch Refaktorisierungen zu reduzieren (Abb. 3). Den meisten Teams ist nicht bewusst, dass es sich auch bei Testautomatisierung um Softwareentwicklung handelt, wo der Testcode aufzuräumen ist, da man sonst schnell wieder vor dem Problem schlecht wartbarer Tests steht.

Mit testgetriebener Entwicklung soll langfristig wartbarer, getesteter Code entstehen (Abb. 3).


Dieser Ansatz erscheint zunächst dogmatisch, doch zum Lernen von testgetriebener Entwicklung ist er bewusst so gewählt. Wenn nach einiger Zeit Sicherheit in gewissen Bereichen eintritt, lässt sich auch versuchen, in größeren Schritten zu implementieren. Es sollte jedoch klar sein, dass ein zu großer Schritt das Risiko birgt, sich zu verrennen, und der Test dann nicht schnell wieder zum Laufen kommt. Tritt dieser Fall ein, sollte das Problem wieder in kleinere Einheiten heruntergebrochen und so gelöst werden.

Ein neuer Trend in den Entwicklungsmethoden geht derzeit in Richtung Acceptancetest-driven Development (ATDD) und Behavior-driven Development (BDD). Im Kern bezeichnet BDD die Vereinigung von TDD, ATDD, Domain-driven Design, Outside-In Development und der Verwendung einer ubiquitären Sprache, die durch Instrumente wie Spezifikations-Workshops entsteht. In der Praxis wird leider häufig ATDD mit BDD verwechselt.

ATDD soll helfen, die richtigen Funktionen zu entwickeln (Abb. 4).

Bei ATDD (vgl. [4]) handelt es sich um eine Maßnahme, um Anforderungen möglichst korrekt im Code umzusetzen (Abb. 4). Die Arbeit beginnt hier bereits früh, noch während die eigentlichen Anforderungen definiert werden. Vertreter aus dem Entwicklungs- und dem Testteam kommen dann mit dem Kunden zusammen und identifizieren Akzeptanzkriterien für zukünftige Funktionen (Abb. 5). Dabei erlangen Tester und Programmierer ein gemeinsames Verständnis des Kundenbedürfnisses, wobei ein genügend großer Raum für unterschiedliche Designentscheidungen offen bleibt (vgl. [5]). Damit können Programmierer und Tester gemeinsam eine Lösung für das Problem des Kunden finden, mit der dieser langfristig zufrieden sein kann.

Das gesamte Team aus Kunden, Programmierern und Testern sollte ein gemeinsames Verständnis der entstehenden Funktionen haben (Abb. 5).


Ausgehend von den erwähnten Akzeptanzkriterien arbeitet das Entwicklungsteam zunächst an beschreibenden Beispielen, die sie vor dem Umsetzen der Funktion allerdings noch weiter verfeinern. Die Beispiele drücken dabei prägnant und exakt aus, was durch die Funktionen erreicht werden soll. Sie bewegen sich auf einem Level mit den Geschäftsanforderungen und sollten nichts mit Umsetzungsdetails wie der Position von GUI-Elementen zu tun haben.

Parallel zur Umsetzung der Funktionen arbeitet das Team dann daran, die Beispiele zu automatisieren. Einige Entwicklergruppen sind sogar in der Lage, die Geschäftslogik von den Geschäftsbeispielen ausgehend in ihren Domänencode zu integrieren. Die Literatur spricht hier von Outside-In Development: Die Entwicklung wird von außen nach innen getrieben. Dabei wird nur so viel Domänencode und Applikationslogik entworfen und geschrieben, wie unbedingt notwendig ist. Praktisch gibt es derzeit nur wenige Teams, die einen derartigen Reifegrad erreicht haben. Die Vorteile dieser Vorgehensweise sind allerdings nicht von der Hand zu weisen: Lässt sich schon vor dem Entwickeln belegen, dass die geplante Anwendung das ausführen wird, was sie soll, dann ist die Flexibilität gegenüber Änderungen in den Anforderungen langfristig gesichert.

Insgesamt bilden TDD und ATDD eine Symbiose, mit der sich ein hoher Grad an Testautomatisierung erreichen lässt. So ist durch ATDD eine Anforderungsabdeckung nahe 100 Prozent möglich, während mit TDD 100 Prozent Code-Abdeckung denkbar ist. In Kombination stellt TDD damit sicher, dass der Code richtig geschrieben wurde, während ATDD dafür sorgt, dass es auch der richtige Code ist.