Agile / Software-Entwicklung / Testautomatisierung

Testgetriebene Entwicklung – Qualität mit Baby Steps von Anfang an

Die Software Quality Days 2016 standen unter dem Themenschwerpunkt „The Future of Systems- and Software-Development: Build in Quality & Efficiency Right from the Start“. In einen kurzen Track habe ich versucht zu zeigen wie dies mit testgetriebener Entwicklung bzw. Test Driven Development (TDD) effektiv erreicht werden kann. Neben dem Qualitätsaspekt war mir wichtig zu zeigen, wie viele kleine Schritte – ich nenne sie gerne „Baby Steps“ – es benötigt, um mit jedem einzelnen Schritt die Entwicklung voran zu treiben.

Ребёнок ползет по лестнице

 

Test Driven Development (TDD)

Wenn man nach TDD sucht, dann findet man meistens die folgende Grafik. Sie erklärt den allgemeinen Workflow der in TDD eingehalten werden sollte:

 

RedGreen1_Test Driven Development

Abb. 1

 

  • ROT: Implementiere einen Test für die nächste Funktionalität der zuerst fehlschlägt.
  • GRÜN: Implementiere so wenig Code wie du benötigst so dass der Test erfolgreich durchgeführt werden kann.
  • REFACTOR: Verbessere den neuen und den bereits erstellten Code.

Die Betonung bei ROT liegt bei einem Test. Die Schritte müssen für jeden einzelnen Test wiederholt werden und zwar so lange bis die gesamte gewünschte Funktionalität umgesetzt wurde.

Was in der Abb. 1 vergessen wurde ist ein weiterer Schritt der zusätzlich iterierend notwendig ist: das Planen der drei genannten Schritte. Der Workflow sieht also richtigerweise so aus (diese Grafik sah ich zum ersten Mal auf der XP 2013 bei einem Vortrag von Emily Bache):

 

RedGreen2_Test Driven Development

Abb. 2

 

Code, Baby Steps und FizzBuzz

Hm, immer noch ein bisschen abstrakt. Der Prozess und seine Schritte scheinen klar zu sein aber was hat das mit Baby Steps zu tun? Und was ist mit Baby Steps überhaupt gemeint? Der Code soll wachsen vom Einfachen zum Komplexen. Wir lernen zuerst zu gehen, dann zu laufen und schließlich zu springen. Jeder Schritt sollte sicher sein, aber auch eine kleine Steigerung ermöglichen – in der Programmierung bedeutet es, dass der Zeitraum wo ein Test rot ist so kurz wie möglich sein sollte. Wir wollen das mit kleinen überlegten Änderungen erreichen und nicht alles wegwerfen, um danach wieder bei Null anfangen zu müssen.

Am besten kann ich das mit einem einfachen Beispiel erklären. Nehmen wir an wir müssen das Spiel „FizzBuzz“ implementieren.

FizzBuzz ist ein Spiel in dem Zahlen von 1 aufwärts gezählt werden. Klingt simple oder? Allerdings gibt es ein paar zusätzliche Regeln:

  • Ist die Zahl durch 3 teilbar => „Fizz“
  • Ist die Zahl durch 5 teilbar => „Buzz“
  • Ist die Zahl 3 und 5 teilbar => „FizzBuzz“

Ein einfaches Programm könnte zum Beispiel folgende Ausgabe generieren:

FizzBuzzOutput

 

Gut beginnen wir mit dem ersten Schritt in unserem TDD Workflow – wir planen. Dazu erstelle ich als erstes eine Liste von Beispielen für die ersten zu implementierenden Tests aus der Anleitung, meine To-do-Liste:

FizzBuzz1

Neben den erwähnten Fällen in der Spezifikation enthält die Liste ein weiteres Beispiel für den nicht aufgezählten Fall „1“. Dieser ist der Standardfall und gehört unbedingt auf die Liste. Aus dieser To-do-Liste lässt sich auch unser API ableiten. Dies führt zu folgendem simplen Design:

FizzBuzzUML

Zu beachten ist, dass die Eingabe eine ganzzahlige Zahl ist und die Ausgabe eine Zeichenkette (in Java der Typ String).

 

Nächster Schritt: wir implementieren einen Test. Wie bereits am Anfang erklärt implementiere ich nicht alle Tests, sondern ich wähle einen einzigen aus meiner To-do-Liste aus.

 

Damit unser Unit Test ohne Fehler kompiliert fehlt uns eine Implementierung. Der Code erfüllt nur unser Design und noch keine Anforderung.

 

Wie erwartet schlägt der Test fehlt. Das ist sehr wichtig, sonst können wir nicht prüfen, ob der im nächsten Schritt entwickelte Code richtig ist.

 

RedUnitTest

 

Jetzt erfolgt unser erster Baby Step in der Implementierung. Dabei kann sich der Entwickler an der Transformation Priority Premise von Robert Martin orientieren.

 

In meinen Kursen und Präsentationen führt das meistens zu großem Unverständnis. Meist die erste Reaktion des Publikums: „Das kann doch nicht sein ernst sein!“ Ist es aber, denn wir sind ja noch nicht fertig. Aber hallo, ich kann Ihnen die Lösung einfach ansagen und dann ist sie fertig. Ja das wäre der klassische Workflow, aber wir wollen Schritt für Schritt vorgehen und den Code durch einzelne Tests vorantreiben. Denken Sie daran, die nächsten Tests müssen wieder fehlschlagen. Alles auf einmal zu programmieren wäre kein Baby Step sondern ein Stabhochsprung. Kent Beck nennt dieses Prinzip in seinem Buch Test-Driven Development by Example: „Fake it till you make it“.

Um den Workflow zu beenden müssen wir uns überlegen ob es notwendig ist einen Refactoring Schritt durchzuführen. In diesem Beispiel gibt es, obwohl wir nur so wenig Code entwickelt haben, eine Kleinigkeit die verbessert werden sollte. Der Name des Eingabeparameters ist „i“ und sollte korrigiert werden.

 

Unser nächster Schritt ist die Planung. Was fällt uns bei unserer Lösung auf? Mit diesem Code erfüllen wir nicht die Anforderung alle natürlichen Zahlen in eine Zeichenkette umwandeln zu können! Wir brauchen also einen weiteren Test und aktualisieren unsere To-do-Liste.

FizzBuzz2

Als nächstes implementieren wir unseren nächsten Test und da nehme ich gleich den Test den wir als letztes hinzugefügt haben, also 2.

 

Wir erwartet schlägt auch dieser Test fehl. Die beiden ersten Tests sind ausreichend um sich zu überlegen wie man am besten eine Lösung für alle natürlichen Zahlen entwickelt. Der Code sollte mit jedem Test komplexer werden und den Lösungsraum vergrößern.

 

Ich aktualisiere meine To-do-Liste, denn ich finde keine weiteren Refactoring Möglichkeiten.

FizzBuzz3

Ich glaube man kann sich nun sehr gut vorstellen was unter Baby Steps verstanden wird. Die nächsten Schritte erfolgen im selben Muster wie die vorigen. Eine mögliche komplette Implementierung können Sie im folgenden Video verfolgen:

 

Fazit

Testgetriebene Entwicklung ist keine Praktik um zu testen, sondern eine Methode um Code in kleinen Schritten begleitet von Tests wachsen zu lassen. TDD bietet einige Vorteile und ist ein effektives und effizientes Mittel zur Erstellung von Software mit dem Anspruch Qualität von Anfang an zu erfüllen. Der implementierte Code ist zu 100% getestet und enthält keine unnötigen Ausschweifungen, die nicht in den Anforderungen enthalten sind. TDD unterstützt Projekte um Fehler durch die Entwicklung sehr früh zu vermeiden. Dieser positive Effekt reduziert die Gesamtkosten eines Projekts signifikant, denn Korrekturen von Fehlern in späteren Phasen führen zu einer exponentiellen Kostensteigerung. (Barry W. Boehm, Industrial Software Metrics: A Top 10 List).

 

Veranstaltungstipp – Training:

iSQI® Certified Agile Test Driven Development (ATDD)
14.6.-16.6.2016, Wien
> Infos & Anmeldung

 

Passende Artikel

Antwort schreiben

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.

*