Best Practices für beste Qualität

Best Practices für Test-driven Development
Keine Kommentare

Wenn sich das Management entscheidet, Test-driven Development (TDD) einzuführen, ist das Ziel klar: neue Softwaresysteme innerhalb kürzester Zeit bei hoher Qualität ausliefern. Doch was oft gut startet, bekommt nicht selten schnell Ermüdungserscheinungen. Dieser Artikel zeigt, wie TDD nicht an Schwung verliert.

Die Idee von Test-driven Development ist nicht neu, aber heute aktueller denn je. Bereits 2002 stellte Kent Beck das Konzept des testgetriebenen Entwickelns vor. Mike Cohn lieferte 2010 fünf Gründe, weshalb aus seiner Sicht das Testen im Nachgang nicht funktioniert: Erstens gibt es im Nachhinein keine Möglichkeit, die Qualität eines Produkts zu verbessern. Zweitens bleiben manche Fehler unentdeckt und passieren deshalb immer wieder. Drittens lässt sich der Projektstatus nur schwer messen, klar ist lediglich der Stand der Featureumsetzung. Welche Qualität diese hat oder wie aufwendig die Fehlerabstellung ist, bleibt hingegen im Dunkeln. Schließlich kann viertens das Entwicklerteam nicht von Feedbackschleifen profitieren und bleiben fünftens nachgelagerte Tests aus Zeitgründen häufig auf der Strecke.

Viel effizienter ist da das kontinuierliche Testen der Applikation. Die Qualitätskontrolle wird damit fester Bestandteil des Entwicklungsprozesses, was u. a. zur Folge hat, dass der Testaufwand über die gesamte Projektlaufzeit gleichbleibt. Ein wichtiger Aspekt ist dabei die Verteilung der automatisierten Tests auf die richtigen Ebenen. Cohn beschreibt dafür eine Testpyramide. Den größten Anteil machen demnach die Modultests oder Unit-Tests aus, während der kleinste Teil aus automatisierten Integrations- und Oberflächentests besteht. Die Zahl der zu entwickelnden Tests nimmt also zur Spitze der Pyramide hin ab, während Komplexität, Ausführungsgeschwindigkeit und Kosten zunehmen.

Bei TDD schreiben die Entwickler erst den Test für eine Funktionalität und dann den eigentlichen Produktivcode. Das inkrementelle Vorgehen aus kleinen, nur wenige Minuten dauernden Tests und der Implementierung des Codes sorgt dafür, dass Testen und Entwickeln eng miteinander verzahnt sind. Auf den Test folgt das Schreiben des Produktivcodes und das Code Refactoring. Diese Vorgehensweise bleibt zwar immer gleich, damit TDD zum Erfolg wird, lohnt es sich aber, einige Punkte im Blick zu haben.

Organisatorische Faktoren

Das Entwicklerteam sollte von Anfang an stark in den Betrieb der Software eingebunden sein. Das hat in den meisten Fällen den positiven Aspekt, dass sich die Developer deutlich mehr für die Qualität des gelieferten Produkts einsetzen und offen für die Einführung von TDD sind. In regelmäßigen Runden zum Erfahrungsaustausch kann das Team Herausforderungen diskutieren und die Qualität der Tests analysieren. Das bewirkt eine kontinuierliche Sensibilisierung für das Thema und führt dazu, dass die testgetriebene Entwicklung nicht über kurz oder lang einschläft. Nicht jeder Entwickler freut sich indes über die neue Arbeitsweise. Hier kann das Pair Programming helfen, den Einstieg zu erleichtern: Zwei Programmierer entwickeln gemeinsam an einem Rechner, wobei ein in der testgetriebenen Entwicklung erfahrener Kollege einen weniger erfahrenen unterstützen kann. Der kontinuierliche Austausch und die regelmäßige Diskussion über die Lösungsansätze sorgen obendrein für bessere Tests und Codes. Aus organisatorischen Gesichtspunkten tragen außerdem zwei Faktoren maßgeblich zum Gelingen einer TDD-Einführung bei: die Unterstützung durch das Management und die Überzeugung des Entwicklerteams.

Technische Faktoren

Aus technischer Sicht ist isoliertes Testen von Vorteil. Das heißt, die Entwickler müssen in der Lage sein, jeden Unit-Test unabhängig von anderen Modulen auszuführen. Sollten dennoch Abhängigkeiten bestehen, kommen Mock-Objekte zum Einsatz. Dieses Vorgehen sorgt für eine hohe Stabilität der Tests gegenüber Änderungen. Darüber hinaus sollten sich die Unit-Tests auch schnell ausführen lassen. Dauern sie zu lange, besteht die Gefahr, dass Entwickler auf die Tests verzichten. Ideal ist eine Dauer von nur wenigen Millisekunden. Die Tests müssen außerdem eindeutig zuordenbar, also nur für ein einziges Verhalten des Produktivcodes zuständig sein. Das Strukturieren der Tests nach der sogenannten Triple-A-Regel stellt diese „Single Responsibility“ sicher (Listing 1). Entwickler gehen dabei stets nach den gleichen Schritten vor: Arrange, Act und Assert. Auf die Initialisierung der Umgebung folgen das Auslösen des Verhaltens und schließlich der Soll-Ist-Vergleich. Ein Abweichen von dieser Reihenfolge ist nicht erlaubt.

def test_calculateArea_simpleValue_calculated(self):
    circle = Circle(center=Point(0,0), radius=1) # 1. Arrange
    area = circle.calculateArea()                # 2. Act
    self.assertEqual(area, math.pi)              # 3. Assert

Strukturieren Entwickler ihre Tests nach der Triple-A-Regel, trägt das auch zur besseren Lesbarkeit bei. Ebenfalls hilfreich ist es, sich auf eine Namenskonvention zu einigen. Das kann beispielsweise der Standard nach Osherove sein, bei dem der Testname stets aus drei Teilen besteht: Name der zu testenden Methode, Szenario, in dem getestet wird, und zu erwartendes Verhalten. Ein letzter technischer Aspekt ist das Refactoring. Das Code Refactoring steht immer am Ende der TDD-Schleife und darf nicht vernachlässigt werden. Es besteht aus dem Refactoring des Produktiv- und des Testcodes. Am besten definiert das Entwicklerteam zu Beginn Coding-Standards, die für alle verpflichtend sind. Hilfreich sind an dieser Stelle auch Tools zur statischen Codeanalyse wie SonarQube, die Abweichungen zu Coding-Standards aufdecken.

Methodische Faktoren

Natürlich passiert es auch immer wieder, dass kein Test stattfindet. Deshalb lohnt es sich, den Abdeckungsgrad der Unit-Tests laufend zu kontrollieren. Liegt er bei etwa 75 bis 85 Prozent, kann das Entwicklerteam zufrieden sein. Eine Steigerung auf 100 Prozent ist insofern nicht erstrebenswert, als dass sich nachweislich die Codequalität dadurch nicht verbessert. Was jedoch hilft, ist die gewünschte Testabdeckung in die „Definition of Done“ aufzunehmen. Diese definiert, wann eine Anforderung als abgeschlossen gilt. Es gibt allerdings auch Fehler im Code, die nicht durch Unit-Tests ans Licht kommen. Geht es etwa um das Zusammenspiel von Modulen, eignen sich Integrationstests besser. Die Gebrauchstauglichkeit zeigen hingegen User-Acceptance-Tests. Es empfiehlt sich also, genau festzulegen, welche Tests auf welcher Ebene der Testpyramide stattfinden sollen. Laut Testpyramide nimmt die Anzahl der Test ab, während gleichzeitig deren Komplexität steigt. In den höheren Ebenen sollte das Team die Tests deshalb mit Sorgfalt auswählen.

Zwei weitere Methoden sorgen dafür, dass TDD gelingt: die Nutzung eines Continuous-Integration-(CI-)Servers und die Einführung von Acceptance Test-driven Development (ATDD). Ein CI-Server stößt automatisiert bei jeder Codeänderung in der zentralen Versionsverwaltung die CI-Pipeline an. Das sorgt für eine regelmäßige, automatisierte Ausführung aller Tests. Die CI-Pipeline läuft wie folgt ab: Kompilieren und Paketieren der Software, Ausführen aller Unit-Tests, statische Codeanalyse. Auch Integrations- und Systemtests sind möglich. Die Entwickler bekommen nach jedem Schritt Feedback zu Fehlern und Seiteneffekten und somit die Chance, diese früh im Prozess zu beheben.

Ergänzend zu TDD bietet sich ATDD an. Hierbei steht das automatisierte Testen der Akzeptanzkriterien und somit die Gebrauchstauglichkeit der Software im Vordergrund. Der Fokus liegt auf der Frage, was eine Anwendung aus Sicht des Users erfüllen muss, damit er sie sinnvoll einsetzen kann. Das setzt voraus, dass die Entwickler die Anwender zu einem frühen Zeitpunkt ins Projekt einbinden. ATDD sollte jedoch nur ergänzend und eng verzahnt mit TDD zum Einsatz kommen.

Fazit

Test-driven Development wird vor allem dann zum Erfolg, wenn zwei Punkte erfüllt sind: Eine effiziente Testverteilung bei einem hohen Automatisierungsgrad. Unter diesen Voraussetzungen schafft TDD nicht nur kurze Releasezyklen, sondern auch bessere Softwarequalität. Beachten die Entwickler einige organisatorische, technische und methodische Aspekte, wird sich TDD auch langfristig etablieren.

Unsere Redaktion empfiehlt:

Relevante Beiträge

Hinterlasse einen Kommentar

Hinterlasse den ersten Kommentar!

avatar
400
  Subscribe  
Benachrichtige mich zu:
X
- Gib Deinen Standort ein -
- or -