4 min read

Warum ich TDD praktiziere und wie es auch dir helfen kann

Mir war schon immer klar, dass Testing wichtig ist, allerdings wurde es von mir dennoch eine lange Zeit vernachlässigt. Wahrend meines Studiums kam das Thema leider viel zu kurz und zudem fehlte es an Praxisbezug. Mit der zunehmenden Berufserfahrung habe ich aber gelernt, dass ich immer mit einer gewissen Fehlerrate und unvorhersehbaren Bugs rechnen muss. TDD ist entscheidend, um diese frühzeitig zu erkennen und auch auf lange Sicht eine gute Produktivität zu erzielen.

Software Testing bei SAP

Während meiner Zeit bei SAP und im ersten Team, in dem ich war, haben wir keine oder nur sehr wenige Unit-Tests geschrieben (ich definitiv keine). Der Fokus lag damals auf End-to-End-Tests (E2E). Später, in einem anderen Team bei SAP, kam ich zum ersten Mal mit Tests und Test Coverage in Berührung, hatte aber immer noch wenig intrinsische Motivation, welche zu schreiben. Ich sah es eher als notwendiges Übel an, mit dem Gedanken einen Test nach der eigentlichen Funktionalität zu schreiben.

Software Testing in der Selbstständigkeit & Open Source

Mit der Gründung meiner Firma LemonHeap GmbH und dem Produkt LemonSpeak habe ich dann vereinzelt und je nach Wichtigkeit Unittests geschrieben, aber von einem TDD (Test Driven Development) Ansatz war ich noch weit entfernt. LemonSpeak war eine Ein-Mann-Show. Daher die Frage: Warum so viele Tests schreiben, wenn ich sowieso der Einzige bin, der die Software entwickelt? Meine damalige Meinung war, dass Tests nur für Software eine Daseinsberechtigung haben, an der mehrere Personen arbeiten. Der Vorteil ist dann, dass man nicht das Gesamtkonstrukt komplett verstehen muss, sondern nur die einzelnen Komponenten, die geändert oder hinzugefügt werden. Die vorhandenen Tests prüfen dann, ob Fehler auftreten und lassen bei einem “pass” den Rückschluss zu, dass es keine Seiteneffekte gibt.

Mein Wandel kam gegen Ende von LemonSpeak, als ich rückblickend bewertete, wie viel Support durch Bugs verursacht wurde und ob ich dies durch Testen hätte vermeiden können. Du ahnst es: Der Großteil hätte vermieden werden können. Zu der Zeit habe ich mich auch mehr mit TDD beschäftigt und mich in das Thema eingearbeitet.

Der zweite Moment, in dem mir die Wichtigkeit von Tests klar wurde, war als ich meine erste Open Source Contribution für Pydantic Logfire gemacht habe. Der erste Pull Request war ohne Tests, der Maintainer sagte mir, ich solle doch Tests hinzufügen und kurz nachdem der Code gemerged wurde, verursachte er einen weiteren Bug bei einem Benutzer. Das war ein echter Aha-Moment für mich, denn wenn die Tests ausgiebig gewesen wären, hätte man das auch finden können. Der User hätte keinen Bug Report geöffnet, der Maintainer hätte mich nicht darauf hingewiesen und ich hätte nicht wieder Zeit investieren müssen, um den Bug zu beheben. Drei Leute waren direkt betroffen. Das zu vermeiden hat für mich etwas mit Professionalität zu tun.

Wie funktioniert TDD?

TDD steht für Test-Driven-Development und ist nicht neu: Das Konzept wurde Ende der 90er Jahre von Kent Beck eingeführt. Die Idee ist folgende:

  1. Du überlegst dir eine Liste an Testbedingungen.
  2. Nimm dir den ersten aus der Liste.
  3. Der zweite Schritt besteht darin, die Testbedingung vor der eigentlichen Funktion zu schreiben und zu überlegen, wann das Ergebnis "pass" und wann das Ergebnis "failed" ist.
  4. Jetzt schreibst du deine Funktion, so dass der Test erfüllt ist.
  5. Optional kannst du im nächsten Schritt dir Gedanken über Abstraktion und Design deines Codes machen und ihn refactoren. Das war bereits ein Zyklus. Wenn du noch Testbedingungen übrig hast, fängt der Zyklus wieder von vorne an (goto #1). Iterationen gehören zu TDD dazu, denn du möchtest dass deine Funktion weitere Testbedingungen erfüllt.

Dieser Zyklus wird im Englischen auch Red-Green-Refactor genannt. Red, weil dein assert zuerst failed. Glückwunsch wenn du bei Green bist, denn dann hat dein Test ein pass erhalten. Der Refactor Step war für mich am Anfang schwer zu verstehen. Hauptsächlich weil es für mich selbstverständlich ist. Sobald du ein green hast, kannst du deinen Code refactoren und anders strukturieren. Sei es ein Pattern oder ein anderer Ansatz. Das liegt ganz bei dir. Das Schöne daran ist, dass du die Sicherheit hast, dass trotzdem alles funktioniert, da deine bisherigen Tests weiterhin durchlaufen müssen. Hier ist eine kleine Visualisierung von TDD:

TDD ist meiner Meinung nach in der Theorie super, braucht aber in der Praxis eine gewisse Wiederholung, um zur Routine zu werden. Martin Fowler hat eine sehr gute Einführung in TDD geschrieben. Noch interessanter ist jedoch der Artikel “Canon TDD” von Kent Beck selbst, in dem er mit einigen Missverständnissen und Irrglauben rund um TDD aufräumt. Durch die Negativbeispiele die Beck aufzeigt, ist der Informationsgehalt sehr hoch.

Meiner Meinung nach liegt der Vorteil von TDD nicht nur in der erhöhten Zuverlässigkeit der Software, sondern auch darin, dass ich intensiv darüber nachdenken muss, wie ich die Schnittstelle zu meinem Code und der Funktion gestalte (Stichwort Abgrenzung Schnittstelle zu Implementierung → gutes Design). Um das zu verdeutlichen: Wenn ich eine neue Funktionalität schreibe, dann zwingt mich TDD dazu, zuerst die Schnittstelle zu definieren, damit ich sie überhaupt testen kann.

Fazit

Es gibt zig Bücher wie beispielsweise Clean Code oder Practical Engineer, die darauf eingehen, dass eine hohe Test Coverage ein Muss ist. Und obwohl ich mittlerweile die Notwendigkeit sehe, würde es mir ohne den TDD-Ansatz schwer fallen, die Tests im Nachhinein zu schreiben.

Denn sobald ich ein Feature entwickelt oder einen Bug behoben habe, wartet schon das nächste Issue um die Ecke. Einen Test für die vorherige Komponente zu schreiben, gerät leider in Vergessenheit. Das ist wie beim Aufräumen zu Hause: Wenn etwas herumliegt, ist es am Ende ordentlicher, wenn man es gleich aufräumt, anstatt die Aufgabe aufzuschieben.

Wie viel und wie intensiv getestet wird, hängt natürlich auch von der Wichtigkeit der Software ab. Aber für eine professionelle Software ist das Testen heute nicht mehr wegzudenken. Ob es sich dabei um Unit-Tests, Integrationstests oder End-to-End-Tests handelt, hängt stark von der Architektur, dem Ziel und der wie erwähnten Wichtigkeit der Software ab.

Mittlerweile habe ich mich sehr gut in TDD eingelebt. In meiner aktuellen Arbeit konnte ich damit schon Bugs während der Entwicklung verhindern, was sich einfach großartig anfühlt. Dennoch ist das Thema in dieser Intensität noch Neuland für mich. Da ich selbst viel in dem Bereich dazulerne, möchte ich dieses Wissen in den nächsten Artikeln mit dir teilen.

Hast du gute Erfahrungen mit TDD gemacht oder siehst du das anders? Kennst du gute Ressourcen? Lass es mich wissen!