Test Driven Development mit PHP – Erste Praxiserfahrungen

Anfangs Jahr stellte ich Euch in einem ersten Artikel das Grundprinzip von TDD (Test Driven Development) vor. Nun habe ich die ersten Praxiserfahrungen gemacht und möchte von diesen Erfahrungen berichten.

Die theoretischen Konzepte hinter TDD habe ich beim lesen schnell verstanden und dachte auch, dass dies eine gute Sache ist. Es kostet aber dennoch einige Überwindung um richtig mit TDD zu beginnen. Nur allzu schnell fällt man in alte Muster und entwickelt einfach drauf los, weil man sich noch nicht so gut mit TDD und dem Testing Framework auskennt.

Folgende Entwicklungsschritte und Erkenntnisse habe ich durchlaufen:

Schritt 1 – TDD in der Theorie

Theorie über TDD gelesen und gedacht: “Wow, das erscheint mir logisch, das muss ich auch mal ausprobieren”.

Schritt 2 – Fizzbuzz mit TDD entwickelt

Nachdem ich Bücher gelesen und einige Tutorials gesehen habe, habe ich mich an das erste reale Übungsszenario gemacht. Die Umsetzung der simplen Aufgabe Fizzbuzz fiel mir recht leicht und meine Erkenntnis zu diesem Zeitpunkt war, dass TDD relativ easy ist 🙂

Schritt 3 – TDD mit Brownfield Code

Voller Elan ging ich in mein Geschäft und dachte mir: “So, fangen wir mit TDD an”.

Relativ schnell bemerkte ich, dass ich es irgendwie nicht fertig brachte, Tests für meine zu testenden Funktionen und Klassen zu formulieren. Diese waren so stark gekoppelt, so dass ich nicht einzelne Funktionalitäten testen konnte, sondern alles instanzieren und einbinden musste. Sinn und Zweck von TDD ist ja, dass man ganz spezifische Funktionen testet und eben nicht das, was sonst noch so alles dran hängt.

Ausserdem bemerkte ich, dass ich mich in der Anwendung des Testing Frameworks noch unsicher fühle (Ich kannte zwar einige Funktionen, dachte aber, dass es da wohl noch mehr gibt 🙂 ). Daher nahm ich mir vor, zuerst nochmals das PHPUnit Manual durchzuackern, vielleicht ergeben sich dadurch einige Erkenntnisse, wie man gekoppelte Funktionen testen kann.

Schritt 4 – Das PHPUnit Manual

Ok, wieder zurück zur Theorie. Habe das PHPUnit Manual durchgeackert und herausgefunden, dass es doch noch 2 oder 3 gute Funktionen neben $blubb->AssertEquals() gibt 🙂

Eine wichtige Erkenntnis war, dass man Klassen und Funktionen “mocken” kann. Das bedeutet, dass man für ganz bestimmte Funktionsaufrufe definieren kann, was man für Ergebnisse zurückerwartet.

Das “Mocken” ist zum Beispiel sinnvoll, wenn man mit Daten aus einer Datenbank arbeitet. Wenn man Tests formuliert, weiss man ja nicht wirklich, was genau in der Datenbank steht und die Tests sollen ja auch  nicht davon abhängen,wenn man die Funktion, welche diese Daten verarbeitet sollen, testen will. Also definiert man die Rückgabewerte der Datenbankwerte einfach mittels mocks.

Schritt 5 – TDD mit Brownfield Code – Part 2

Ok, wieder zurück im Geschäft und bereit für’s mocken. Habe zuerst einmal den Datenbanklayer gemockt und dann die ersten Tests geschrieben, mit Erfolg. Nachdem ich aber einige Funktionen und Objekte gemockt habe, setzte sich die Erkenntnis durch, dass es trotzdem schwierig ist, Tests für bestehenden Code zu schreiben.

Warum?

1. Der Code ist untereinander so verknüpft und teilweise auch unsauber, dass es sehr viel Zeit benötigt überhaupt einen vernünftigen Test zu generieren

2. Durch den Zeitdruck sagt man sich schnell mal: “ok, dass mach ich jetzt noch kurz ohne TDD, ich habe ja Stress”.

Schritt 7 – TDD mit Brownfield Code – Refactoring

Ich beschäftigte mich weiterhin in der Theorie mit TDD und Clean Code. Bei der Arbeit schaffte ich es durch den Zeitdruck aber noch nicht, konsequent TDD anzuwenden.

Inzwischen hatte ich für einige Funktionen Tests geschrieben und diese waren auch alle grün. An einem Wochenende startete ich eine Übungssession für TDD und nahm mir den Brownfield Code vom Geschäft vor. Da ich überhaupt nicht zufrieden mit dem Code war, entschied ich mich für ein Refactoring einer Klasse, für welche bereits Tests bestanden. Zu Hause hatte ich ja auch keine Datenbankanbindung und auch keine saubere Umgebung. Somit konnte ich beim Refactoren die Applikation nie ausführen (also im Browser laufen lassen).

Ich war also am Refactoren und meine Tests waren stets erfolgreich (nach einigem Üben). Am Montag wieder zurück im Geschäft spielte ich die neue Klasse ein und “bäm” die Refactorte Klasse lief ohne Probleme.

Das war für mich endgültig die Erkenntnis, dass es ohne TDD nicht mehr geht und dass man mit TDD einfach bessere und fehlerfreiere Software entwickeln kann.

Erkenntnisse

Zitat Robert C. Martin: “Nur Software die getestet werden kann ist gute Software”.

Im Endeffekt geht es nur darum gute Software zu entwickeln. Welches Instrument man schlussendlich verwendet ist egal. Meine persönliche Erkenntnis ist aber, dass TDD die Entwicklung von guter Software ermöglicht.

  • TDD fördert die Testbarkeit, weil man vorher schon den Tests dafür schreibt
  • TDD fördert entkoppelte und komponentenbasierte Software und ergibt somit bessere Software
  • TDD auf der grünen Wiese ist deutlich einfacher, als mit Brownfield Code
  • TDD muss man einfach ausprobieren und anwenden – am Anfang muss man sich konsequent zwingen es anzuwenden. Mit der Zeit ergibt sich ein aha-Effekt in der Praxis und man sieht, dass TDD unglaubliche Vorteile gegenüber der normalen Entwicklung hat
    • Man entwickelt entkoppelte Funktionen
    • Man fühlt sich sicherer, weil man Tests hat
    • Bestehender Code kann ohne Probleme angepasst werden, weil man jederzeit weiss, dass die alte Funktionalität noch gegeben ist
  • Wenn man konsequent mit TDD entwickelt, kann man eine Funktion in einem Brownfieldprojekt erst Refactoren, wenn man für die alte Funktion zuerst Tests geschrieben hat. Nur so kann die bestehende Funktionalität sichergestellt werden
  • Ohne das Testing Framework gut zu kennen (in meinem Fall PHPUnit) kann man nicht mit TDD entwickeln

9 Responses

  1. Aller Anfang ist schwer. Ähnliche Erfahrungen habe ich auch gesammelt, als ich angefangen habe mich mit TDD zu beschäftigen. Der Einsatz von TDD in Brownfieldprojekten ist wohl eine der schwersten Übungen überhaupt. Meist handelt es sich um gewachsene Strukturen, die einfach nicht testbar sind. Dannn muss man auch schon mal in den sauren Apfel beißen und Refactoring an nicht getesteten Klassen vornehmen, damit man diese Klassen überhaupt erst testen kann. Allerdings darf man sich nicht daran gewöhnen 🙂

    Mir bleibt da nur ein Faizit: Keine Neuentwicklung mehr ohne TDD 🙂

  2. @anrichter – ja, für Neuentwicklungen ist TDD ein absolutes muss.

    Auf folgende Probleme werden “wir” aber in den nächsten 5-10 Jahren wohl massenhaft treffen:
    – Entwickler / Manager, die den Bedarf und den Wert von TDD(oder auch BDD) noch nicht einsehen
    – Massenhaft Brownfield Code! Und mit diesem Code muss man auch lernen umzugehen, bzw. man muss Techniken entwickeln, wie man diesen Code in schönen Code umwandelt, also Refactoring betreibt.

  3. Ich kann deine Schwierigkeiten sehr gut nachvollziehen. Gerade für alten Code kann man die neuen Erkenntnisse häufig nicht sofort anwenden, da fehlt immer noch was, nämlich die Kunst des kontinuierlichen Refaktorierens. (Nein, den Buchtipp wiederhole ich jetzt nicht nochmal ;))
    Aber auch TDD ist eine langfristige Sache, wenn es um das Erlernen geht, daher ja auch meine eher längerfristig geplantes Blog rund um das Thema. Vielleicht willst du ja mal einen Gastartikel verfassen?

  4. Hi Christian

    Ja, Dein Buchtipp (ich nehme mal an Du meinst “Arbeiten mit Legacy Code von von Michael Feathers) hab ich schon vorbestellt (deutsche Version).

    TDD macht einfach Spass und vermittelt auch positive Erlebnisse (man testet etwas, das funktioniert bzw. grün wird), ganz entgegen von negativen Erlebnissen (ich finde einen Bug und muss ihn fixen).

    Ich verfolge Deinen Blog regelmässig und finde Deine Artikel super. Wenn ich mal was interessantes zu berichten habe, wäre ich durchaus bereit mal einen Artikel zu verfassen.

  5. Ich finde es persönlich extrem schwer zuerst einen Test zuschreiben und dann die eigentliche Implementierung. Der andere Weg fällt mir momentan um einiges leichter. Dass man Tests schreibt steht für mich außer Frage! Hast du tests-first mal probiert?

    Zum Thema Refactoring kann ich die Aussage eines Kollegen anbringen: “Refactroing ist nichts Schlimmes”. Wenn das in allen Köpfen angekommen ist, steigt auch die Akzeptanz für Tests.

    Von Fowler gibt es ein ganz gutes Buch zum Thema Refactoring.

    Grüße

  6. Hi Norbert

    Danke für Dein Feedback.

    Ja, ich tue mich momentan auch noch schwer mit dem Test-first Ansatz. Aber ich versuche mich immer dazu zu überwinden bzw. zu zwingen 🙂

    Refactoring von Martin Fowler ist bereits auf meinem Nachttisch. Ich muss aber zuerst noch mit Clean Code und PHP Design Patterns fertig werden 🙂

Leave a Reply

Your email address will not be published. Required fields are marked *