Es tobt ein Religionskrieg im PHP-Land! Ihr glaubt es nicht? Dann outet Euch mal in der PHP-Community als Unit-Tester von protected/private Methoden. Die Mehrheit unserer PHPUG tut es hiermit. Wir tun es auch schon recht lange und fahren gut damit. Und wir haben auch unsere Gründe es genauso zu tun. Wir sind keineswegs allein mit unserer Ansicht.
Doch zunächst zu den vermeintlichen Gegenargumenten. Fragt man einen Vertreter der anderen Seite, warum man es nicht tun sollte, hört man grundsätzlich nur ein Argument: “Man testet nur das public interface einer Klasse.” “Eine Klasse ist eine black box, was intern passiert interessiert nicht.” Hinterfragt man dann konkret, warum man nur das public interface testen soll, bekommt man außer Verweise auf Leute, die es so propagieren, kaum fundierte Argumente.
Allenfalls noch “So viel Zeit haben wir nicht, auch protected/private Methoden zu testen.” Kommt Euch das bekannt vor?
Genau, das sind auch die Ausreden der Leute, die Unit-Tests grundsätzlich für überflüssig halten.
Wir setzen Unit-Tests ein, um möglichst fehlerfreie und verlässliche Software für die Praxis zu schreiben. Und genau deswegen testen wir auch private/protected Methoden. Die eifrig propagierte Alternative dazu ist, nur das public interface und den Code der protected/private Methoden damit nur indirekt über die public Methoden zu testen. Doch Hand auf’s Herz: was ist naheliegender?
In diesem Punkt widersprechen sich die Vertreter der anderen Seite übrigens mehrfach selbst: es ist bei Unit-Tests zum Einen eben sehr wohl von Belang, was innerhalb der Klasse geschieht, auch und besonders in den protected/private Methoden.
Zum Anderen gehören protected Methoden zwar nicht direkt zum public interface, sie können aber innerhalb der Vererbungskette mehr oder weniger doch von außen aufgerufen werden. Und dann würde eben eine Methode außerhalb der deklarierenden Klasse eingesetzt, die nur indirekt getestet wurde.
Das Testen von private/protected Methoden – noch ein gern genommenes Gegenargument – bedeutet auch ganz und gar nicht, das Klassendesign an das Unit-Testing anzupassen. Das wäre sicherlich falsch und das muss man auch nicht, wenn man das Proxy design pattern und Reflection einsetzt.
Das einzige plausible Gegenargument, das uns z. B. bei Zend aufgefallen ist, ist die Tatsache, dass beim Refactoring gerade private Methoden gern mal schnell geändert und – was wesentlich problematischer für unsere Methode ist – entfernt werden.
Das ist in der Tat nicht so ohne weiteres von der Hand zu weisen. In diesem – und nur in diesem Fall – hätten wir einen Unit-Test, den wir anpassen oder sogar wieder entfernen müssten. Doch auch hier sollte man kritisch hinterfragen, wie oft man Klassen einem derartigen Refactoring unterzieht, wie oft man selbst das public interface ändert oder Unit-Tests für das public interface in der Praxis ändern oder anpassen muss. Ehrlicherweise entschärft sich dann dieses Gegenargument recht schnell von allein. Ein Hindernis für das grundsätzliche Refactoring einer Klasse ist unsere Methode jedenfalls nicht.
Und ein weiterer Punkt wird bei dieser Diskussion außer Acht gelassen: der Unit-Test-Code, auch wenn er heutzutage in guten Open-Source-Projekten mitgeliefert wird, gehört nicht zum Applikationscode. Das kann man nicht deutlich genug sagen. Natürlich sollte man auch hier grundlegende Programmierregeln einhalten. Das bedeutet aber nicht, dass man zum Testen nicht doch einige Regeln bewusst brechen darf, insofern es bessere Tests ermöglicht (Beispiel: direkter Zugriff auf $_GET oder $_POST). Auch die Vertreter der anderen Seite tun soetwas. Warum also nicht auch das Dogma “public interface” beim Unit-Testing (und nur dort) in Frage stellen, wenn es keine schwerwiegenden Gegenargumente gibt und fast nur Vorteile bringt?
Das Testen von private/protected Methoden hat übrigens noch einen anderen positiven Effekt: man findet Bugs wesentlich schneller. Probiert es einfach aus.
Die Einführung von ReflectionMethod::setAccessible in PHP5.3 zeigt, dass dieser Bedarf in der Praxis offensichtlich besteht. Nicht ohne Grund haben auch wir ein kleines Tool, mit dem man auch ohne PHP5.3 sowohl protected als auch private Methoden testen kann, den SuperProxy.
In diesem Sinne: traut Euch einfach mal, neue Wege zu gehen! Es tut nicht weh…