Voraussichtliche Lesedauer: 9 Minuten

Ich glaube jeder Entwickler kennt es und ich bin mir sicher, sehr viele Entwickler betrifft dieser Artikel über Tests. Sehr viele Entwickler denken nicht mal daran, Ihren Code mit PHPUnit zu testen geschweige denn überhaupt irgendwelche Tests zu schreiben. Und erstrecht schon nicht, eine hohe Testabdeckung (auch Code-Coverage gen.) zu erreichen. Selbstverständlich gibt es auch Entwickler, die sich einen Sport daraus machen, eine 100%ige Coverage zu erreichen. Ob dies so sinnvoll ist?

Bis vor 4 oder 5 Jahren habe ich ebenfalls wie sehr viele andere PHP Entwickler nichtmal über Testing nachgedacht. Geschweige denn überhaupt richtig verstanden, wie man Unittests schreibt. Natürlich kannte ich namentlich PHPUnit und wußte auch, was man damit macht. Ich habe aber selbst bis dato nie einen einzigen Test selbst geschrieben (wenn überhaupt, dann vielleicht mal ausgeführt).

Demzufolge war es mir auch absolut nicht wichtig Tests zu schreiben. Ich testete wie immer nur auf Sicht mit Aufrufen der Seite. Zumindest habe ich aber insbesondere bei Formularen auch mal ungewöhnliche Eingaben gemacht. Schließlich sollte man auch testen, ob XSS abgefangen wird. Oder SQL-Queries wie das berühmte Union via Forms ausgeführt werden.

Dass ich dadurch mir eigentlich mehr Bugs angelacht habe kam mir überhaupt nicht in den Sinn. Erst recht, je größer das Projekt war. Zugegeben, die damaligen Auftraggeber hätten wohl auch die Zeit um Unit- & Integrationtests zu schreiben nicht bezahlt. Andererseits ist das eigentlich mit eine Sache, wie man eben seine Software btw. Leistung verkauft.

Eine ganz neue Welt mit Tests tut sich auf

Allerdings merkte ich vor einigen Jahren wie vorteilhaft Unittests sind. Ich beschäftigte mich nur interessehalber mit Testgetriebene Entwicklung. Dabei stellte ich an eigenen Projekten mit objektorientiertem Code fest, wie viel zusätzlichen Mist in einem Objekt enthalten war. Völlig unnöitg. Bei den Beispielen habe ich immer einen Rückschluss auf eigene Projekte gezogen. Danach habe ich direkt versucht, einzelne Objekte zu refaktorieren.

Ganz im Sinne von TDD habe ich schlicht das Objekt nochmal neu umgesetzt und bin diesmal Schritt für Schritt vorgegangen. Ich stellte schlussendlich fest, dass ich gut die Hälfte des alten Objekts umgesetzt habe. Aber, dabei habe ich die gleiche Funktionalität erreicht (und wer weiß, welche Bugs sogar dabei entfernt wurden).

Mir kam die Erkenntnis, dass man sich viel intensiver mit den einzelnen Objekten auseinandersetzt. Auch ergab es für mich nun viel mehr Sinn, wieso man in viel mehr kleineren Klassen die Objekte auseinandernimmt. Spinnt man das nun weiter auf das Projekt bzw. kleinere Projektteile, erhält man dadurch viel stabileren und nachhaltigeren Code.

Nebeneffekt: Code-Smells verhindern und sauberer Code

Ein richtig cooler Nebeneffekt des Refaktorings mit TDD war auch, dass man den Code genauer inspizierte. Dabei stellte ich nicht selten fest, dass man die einzelnen Methoden und Variablen schlicht besser benamen konnte. Dieser ist dann viel einfacher zu lesen und zu verstehen. Schließlich sollte man keine Tests schreiben, die eine Dokumentation benötigen um zu begreifen, was die überhaupt wie abtesten.

Daneben wurde ich bei den zahlreichen Tutorials auch auf Code-Standards aufmerksam. Damit meine ich z.B. bei Klassen die geschweiften Klammern in die nächste Zeile zu packen, anstatt auf gleicher Höhe. Benamungen von Klassen, Methoden, Variablen CamelCased zu schreiben und sinngemäß zu benennen.

Ganz sicher – und das bemerkte ich vor allem in dem gerade aktuellen Projekt – habe ich diese Art zu entwickeln aber noch nicht verinnerlicht. Damit meine ich gar nicht die testgetriebene Entwicklung, sondern insbesondere auch seinen Code überhaupt zu testen und Code-Smells zu verhindern. Ich erwische mich selbst immer wieder dabei in alte Muster zu verfallen.

Erfahrungen aus meiner freiberuflichen Tätigkeit

Tatsächlich ist mein vergangenes Verhalten in Sachen Testen und unsauberen Code gar nicht so selten. Mit den Entwicklern, mit denen ich die letzten 5 Jahre zu zusammen gearbeitet habe, haben genau die gleichen Probleme wie ich damit gehabt. Selten sah ich Code von einem anderen Entwickler, wo ich direkt dachte: Oha, das ist aber echt mal Code, den man eigentlich sofort versteht. Vor allem wenn man sich die Tests dann anschaut.

Auch kam ich insbesondere bei dem aktuellen Projekt mit Coverage überhaupt erst richtig in Berührung. Bisher war das ehrlich gesagt für mich nur ein Wort, was ich zwar verstand aber nie richtig deutete. Und obgleich ich schon seit mindestens 10 Jahren mit PHPStorm arbeite, löste diese Erkenntnis ein gewaltes Aha bei mir aus.

Plötzlich erkannte ich, wieviel ich in einer Klasse tatsächlich mit einem Unittest abgedeckt habe und was überhaupt nicht getestet wurde. Wenn man sich dann den Code nochmal genau anschaut, hatte ich nicht selten nochmal locker 10% von der Klasse entfernt. Vieles war schlicht unnötiger Mist bzw. konnte durch eine besseren Lösung ersetzt werden.

Die Vielfalt der Tests

Neben Unittests, in dem klassischer Weise auch nur eben Units getestet wird, gibt es ja noch Integration- und Functional- bzw. Akzeptanztests. Wie die Bezeichnungen aber auch schon verraten, liegt der Fokus hier anders als bei den Unittests. Wenn ich da an einige der Freelancer denke, mit den ich zusammenarbeiten durfte, komme ich zum folgendem Ergebnis: Wenn denn überhaupt getestet wird, dann mit ein paar simplen Unittests und zumeist dann noch einige Akzeptanztests mit z.b. Behat, um komplette Logik insbesondere bei MVC-Anwendungen fix mal durchzutesten.

Schaut man sich dann aber mal die Coverage an, kann einem echt oft schlecht werden. Oft stellt man dann fest, dass bei Functionaltests nämlich ne Menge an Code gar nicht wirklich mit abgedeckt wird. Will man dies abdecken, schreibt man zahlreiche Tests und man kann getrost nen Kaffee holen oder gar Mittag machen, wenn man dann die Tests ausführt. Functionaltests sind eben aus Performance Sicht verdammt teuer.

Denkt man dann mal an die automatisierten Tests in Deployments, kann das echt ein gewaltiger Zeitkiller sein. Aber warum ist das so? Wieso schreiben viele nur Ihre Behattests und kaum Unittests?

Ich glaube das hängt mit der allgemeinen Faulheit und Einstellungen einiger Entwickler zusammen oder haben schlicht Druck auf der Pipeline. Sie testen im Grunde nur, weil man testen sollte oder es gar eine Vorgabe seitens des Auftraggebers / Chefs ist.

Sie nutzen Tests aber nicht dafür, Ihren Code qualitativer und somit viel stabiler zu machen. Möglicherweise liegt es auch am Zeitdruck, den der PO hier einem Entwickler aufdrückt. Sowas wie „wieso brauchst Du für das Ticket 2 Tage“ oder „Was andere doch in 5 Stunden erledigen?. Insbesondere die zeitlichen Vorgaben für die Umsetzung eines Tickets sehe ich sehr oft in vielen Betrieben.

Meine Erkenntnis aus diesen Erfahrungen

Tatsächlich schaue ich mir intensiver meinen entwickelten Code an und refaktoriere nach dem Schreiben der Tests noch gewaltig viel. Das kostet natürlich Zeit, dafür ist der Code aber auch viel hochwertiger, stabiler und künftig auch nachhaltiger.

Gerade auch bei MVC Projekten denke ich viel intensiver über mögliche Tests nach. Was auch zur Folge hat, dass ich in noch kleineren Objekte denke, die wiederum noch einfacher zu testen sind.

Auch die Benamungen von Variablen, Methoden, Klassen, Dateien, etc. sowie auch das Coden nach PSR-12 Standards beachte ich viel mehr. Insbesondere wenn man dann einen Editor wie PHPStorm einsetzt, kann es einen sehr sauberen stabilen Code zustande bringen. Setzt man auch noch PHPStan ein, ist der Code auch noch nach einem halben Jahr verständlich.

Die Coverage im Auge behalten

Die Coverage ist nicht zwingend in meinen Code hoch. Ich hatte auch schon Aufgaben, die lediglich eine Coverage von 80 bis 85% erreichte. Aber ich bin mir sicher, dass man Code aus den PHP Binäries (json_last_error und sowas) nicht mehr zwingend testen muss. Ich behaupte mal einfach, dass der Code wohl schon sehr stabil und zuverlässig funktionieren wird ;-)

Auch die Entities und meistens Repositories aus Symfony Projekten müssen sicher nicht getestet werden. Klassen (also z.B. Entities) die lediglich Zuweisungen, Getter und Setter besitzen können kaum schief laufen. Setzt man die standard erzeugten Repos von Symfony Entities ein, muss man auch diese nicht testen. Schreibt man doch spezielle Methoden in den Repos sollte man die aber schon auch testen.

Auch wenn ich in den Symfony Controllern die gesamte Logik in einem Messenger auslagere, brauche ich keine Integrations- / Functionaltests für die Controller mehr. Die Messanges lassen sich auch direkt mit einem Unittest abdecken (und nur das ist auch wichtig). Somit können – wenn man es konsequent so umsetzt – auch die Tests für Controller aus der Coverage rausfallen.

Nun, nicht ganz richtig was ich hier schreibe. Es macht durchaus natürlich Sinn noch einige Behattests zu schreiben die die Akzeptanzkriterien zu testen. Diese kann man dann aber zumindest mal aus den automatisierten Deployments dann herausnehmen (oder eben Jest/Cypress- bzw. Selenium-Tests umsetzen).

Fazit bzw. was will ich überhaupt damit sagen?

Tatsächlich bin ich praktisch von einem Testmuffel mittlerweile zu einem Testnotoriker geworden. Natürlich fehlt mir noch Wissen in Sachen Frontendtests (insbesondere z.B. mit Jest oder Cypress). Aber eben mangels Frontend-Aufgaben beschäftige ich mich später einfach damit, wenn es wieder zu einer Aufgabe für mich wird.

Ich habe sehr viele Erkenntnissen daraus gewonnen und wie sehr man doch auch im Alter noch neues lernen kann. Beruflich bringt mich das ganz bestimmt um längen weiter. Mit dem Wissen, dass doch schon viele meiner freiberuflichen Konkurrenten eben genau das Testen nur stiefmütterlich beherschen, wenn überhaupt.

Sollte ich vielleicht den Artikel nicht veröffentlichen? ;-)