Vor kurzem wurde in der Schneide ein “Test First” Workshop abgehalten. Wir arbeiteten zusammen mit einem ehemaligen Schneide-Mitarbeiter, der sein Wissen direkt vom geistigen Vater des “Test First”-Ansatzes beziehen kann, an einem kleinen aber nicht trivialen Projekt. Trotz einer unsäglichen Zeit früh am Morgen war die Stimmung gut und der Lernwille groß. Natürlich sind wir in keinster Weise fertig geworden, aber erste Eindrücke sind gewonnen.
Was ist Test First?
Im wesentlichen ist Test First eine konkrete Handlungsanweisung für Programmierer, die ihre Software mit einer ordentlichen Testabdeckung versehen wollen und, das ist für mich die beste Erkenntnis gewesen, deren Software vor allem “good enough” (und nicht bedeutend mehr) programmiert und getestet sein soll.
Die Handlungsanweisung kann in sieben Schritte unterteilt werden:
- Schreibe einen Testfall
- Schreibe Code, so dass der Testfall kompiliert
- Lasse den Test laufen, der Testfall wird fehlschlagen
- Schreibe Code, damit der Testfall nicht mehr fehlschlägt
- Lasse alle Tests laufen, es sollte kein Testfall fehlschlagen
- Refaktorisiere
- Mache weiter bei 1.
Natürlich ist dieses Vorgehen nur ergänzt von anderen Zyklen wirklich sinnvoll. Man sollte den richtigen Zeitpunkt für ein Einchecken des Codes, für das Beheben eines Issues und auch für Orientierungen innerhalb der Projektstruktur nicht außer acht lassen. Für den Umgang direkt mit dem Code ist das Vorgehen aber geeignet – vor allem, wenn man noch nicht ganz genau weiß, wie der Code aussehen soll.
Im Workshop sind wir nach einer Standardklasse mit entsprechenden Standardtests relativ schnell zu “interessanteren” Problemen gekommen: Wie testet man parallele Ereignisse? wie testet man Beobachter-Strukturen? welche Bestandteile eines Tests sollte man mit Mock-Objekten realisieren, wo sollte die reale Implementierung zum Einsatz kommen?
Wichtigste Erkenntnis: In der Praxis müssen auch die Tests vor allem “gut genug” sein. Gerade Threads bzw. parallele Abläufe mit asynchronen Ereignissen sind auch im Testcode nur schwierig abzubilden und sollten durch geeignetes Implementierungs-Design so weit wie möglich vermieden werden.
Durch die Vielzahl an Problemstellen beim Testen fokussiert man fast von selbst den Blick auf den Produktivcode ausschließlich vom zu testenden Aspekt her. Die Implementierung und das Design des Produktivcode werden “testabhängig”. Das ist zum einen sehr positiv, ermöglicht es doch gute Testbarkeit. Auf der anderen Seite entsteht unterhalb einer bestimmten Code-Ebene (auf der dann die Integrationstests ansetzen müssen) eine Art “maximaler Freiheitsgrad” für die Verwendung des Codes. Welchen Effekt diese Eigenschaft auf die Stabilität des Projekts hat, kann ich noch nicht beurteilen.
Das Motto, das ich aus diesem ersten Workshop mitgenommen habe: “Schreibe keinen Produktivcode ohne Motivation. Erkläre deine Motivation am besten in einem Test.”
Die Erkenntnis, die ich aus diesem ersten Workshop mitgenommen habe: “Ich will mehr darüber erfahren – in der Praxis, nicht in der Theorie”
Ein großes Dankeschön an Matthias für diesen wundervoll anstrengenden Test First Morgen.