Sie haben sicher auch schon davon gehört:
TDD – Test Driven Development
Ein Mythos? Alle haben schon davon gehört, aber keiner glaubt es so recht? Aber man kennt jemanden, der jemanden kennt …
Das echte Leben
Auf der letzen Symfony Konferenz in Berlin wurde es wieder einmal deutlich: Softwarequalität === Gesundheit, so der Titel des Talks von Roland Golla, über den man mehr erfährt auf blog.nevercodealone.de. Die Kernaussage: Viele Entwickler leiden unter ihren Arbeitsbedingungen. Und die Erkenntnis gegen Ende des Talks (nach der Frage “Wem geht es ebenso?”): Es sind viel zu viele! Von geschätzt 150 Leuten, heben gefühlt alle die Hand! Auf die Gegenfrage, “Bei wem ist es anders/besser?”, melden sich drei. Drei!
Eine echte Alternative …
Tests! Vor allem Unit Tests aus Sicht der Entwickler. Aber auch Component, Integration, Acceptance oder Regression Tests. Hauptsache automatisierbar, damit am Ende so wenig wie möglich manuell passieren muss.
Ja, das Management scheut sich. Ja, gute Tests sind schwer zu schreiben. Ja, man muss besonders diese Fähigkeit mühsam erlernen. Aber es lohnt sich! Ein guter Begleiter auf dem Weg dorthin ist das Buch “xUnit Test Patterns”. Über 800 Seiten. Jede einzelne lesenswert. Großen Respekt an den Autor Gerard Meszaros! Und wie im Buch benannt, braucht es viele “Learning Tests” – dort etwas anders gemeint, aber finden Sie das lieber selber heraus! Warum?
… für ein besseres Leben
Ich erinnere mich gut! Das erstmal, als “alles zerfiel”! Es war fantastisch!
Komplexe Aufgaben, die man nicht im Vorraus vollständig überblicken kann, sind ein Problem. Man ist während des Schreibens der Lösung mit dem Problem beschäftigt. Das ist schwer genug und braucht die volle Aufmerksamkeit. Da ist kein Platz für Design Patterns und Best Practices. Natürlich arbeitet man so strukturiert wie möglich und es wird vielleicht nicht völliger Murks. Aber damit es brauchbar und “haltbar” wird, muss man noch einige Male drüber gehen (refactorn) und Tests schreiben. Manches Mal freut man sich, wenn dann die Einzelteile besser entkoppelt werden und die Tests gut sind. Wenn man denn überhaupt Zeit dafür hat. Oft bleibt es aber auch so. Die Zeit und das Management drängt.
Wenn man aber tatsächlich und konsequent (etwa wie unten beschrieben) per TDD entwickelt, kehrt sich der Prozess plötzlich um. Die Einzelteile entkoppeln sich im Vorfeld, die Tests zwingen sie dazu. Die Tests zwingen auch den Entwickler dazu, sauber zu arbeiten. Alles zerfällt in gut zu handhabende Komponenten, die leicht zu verstehen und schön anzusehen sind. Modularer Code in guter Qualität entsteht, auf den jeder Entwickler zu recht stolz sein kann. Aber vor allem geht das anschließende Refactoring auf ein Minimum zurück, da das meiste der notwendigen Anpassungen bereits während der ersten Implementierung gespart wurde. Wie gesagt: Tests erzwingen gutes Arbeiten. Und dadurch wird man schnell und sicher!
Im Endeffekt werden durch TDD Projekte zeitlich besser planbar. Natürlich auch, weil man “schneller arbeitet”. Das ist aber nicht der wesentliche Punkt. Vor allem entsteht modularer, gut wartbarer Code. Und die ständigen Anpassungen und Erweiterungen fallen somit leicht und sind zeitlich planbar und unproblematisch. Der sonst enorme Faktor “Legacy Code” und das arbeitem mit diesem reduziert sich auf ein Minimum.
Daher stimmt leider die Definition: “Code ohne Tests ist Legacy Code”! Wenn doch bloß das Management das begreift…
Wie kam ich dorthin?
Bis vor einiger Zeit war ich noch der Meinung “Ich schreibe meine Tests lieber nach dem Code”. Kurz danach und mit hoher Coverage, aber danach. TDD liegt mir nicht, dachte ich.
Dann habe ich mich eines Tages dran gesetzt. Inspiriert durch “Uncle Bobs” Talk über “The Transformation Priority Premise” entschloss ich mich, TDD einmal “nach Lehrbuch” zu versuchen. Die Möglichkeiten schienen doch zu verlockend.
Aber, wie beginnen? Na gut, schreib ich mal einen Test. Eine Klasse, eine Methode und ein “assertTrue(true)” später, hatte ich einen grünen Test. Sinnlos, also fragte ich mich “Was will ich den testen?” Verhalten, nicht State, soviel war klar. Na gut, schreib ich halt mal was hin: “sut = new SomeClass(); result = sut.doSomething(); assert(result)”. Ein roter Test, gut. Und plötzlich ging es los:
- Ich musste überlegen, wie ich die Klasse nenne. Woführ ist sie zuständig, diese eine Sache a la SRP?
- Wie soll die Methode heißen? Was soll sie ganau tun, welchen Input (Parameter) braucht sie dafür?
- Vor allem musste ich mir klar werden, was ich als Ergebnis erwarte. Welchen Rückgabewert wollte ich?
Ohne es zu merken begann ich in Design zu denken. Ich war plötzlich in der Lage, all die mir bekannten Best Practices und Design Patterns in Worte (Code) zu fassen. Und es war leicht, weil ich mich noch nicht um die Implementierung kümmern musste, sonder das große ganze beschreiben konnte. Als ich zufrieden war mit der Beschreibung, hatte ich einen aussagekräftigen, roten Test und machte mich an die Umsetzung. In kürzester Zeit hatte ich die Funktionalität fertig und der Test war grün.
Die nächsten Tests fielen mir leichter, die nächtenTage und Wochen habe ich mehr und mehr so gearbeitet und mitlerweile entwickle ich eigentlich nur noch “test driven”. Oft fange ich mit Integrationstests an, welche die Benutzung einer Komponente beschreiben. Diese beinhalten dann etliche Assertions und sind natürlich nicht das Ziel. Aber auf dem Weg zum Ziel finden diese Assertions ihren Weg in die entsprechenden Unit Tests und der Integrationtest schrumpt auf eine gesunde Größe. Er zeigt dann nur noch, dass und wie sich die jeweiligen Komponenten zusammenfügen lassen.
Mittlerweile läuft es fast automatisch. Ich schreibe oft erst ein Haufen Interfaces, welche zeigen “wer wie mit wem” kommuniziert und wo aus Sicht der Architektur Boundaries verlaufen. Meist fürhrt mich mein Weg über Integrationstest über Komponententests zu den eigentlichen Unit Tests. (Gute IDEs sind dabei eine echte Hilfe, daher habe ich auch das gesamte Paket von JetBrains. Und Ich kriege nichts von “denen” sondern bezahle dafür. In meiner Firma und privat. Und das gerne.) Mein Dank gilt dann auch den vielen Autoren, welche sich in den letzten Jahrzehnten die Mühe gemacht haben, ihr Wissen zu verbreiten. Es sind zu viele, um sie alle zu bennen, aber vielleicht sind Robert C. Martin, Martin Fowler, Gerard Meszaros und Greg Young ein paar interesannte Namen.
Das schöne an TDD
Es existiert wirklich!
Man schreibt mehr, aber man entwickelt schneller. Am Ende schreibt wohl weniger, man löscht halt nich mehr so viel. Irgendwie so. Gefühlt brauche ich per TDD die Hälfte oder ein Drittel der Zeit für Aufgaben im Vergleich zum “Tests nach dem Code” Vorgehen. Ich kann mittlerweile fast nicht mehr anders. Und ich kann es endlich aus eigener Erfahrung bestätigen!
Es macht Spaß. Es ist sicher. Es entspannt. Es wird gut.
MyCode extends TestCase bzw. MyTestCase drives MyCode!