PHP Unittests: Wie verwende ich getMock()?

Damit wir unseren Code sinnvoll testen können, müssen wir immer wieder Kopplungen zu anderen Klassen auflösen. Dies geschieht mit Mocks dieser Klassen. Um aber allen Ansprüchen gerecht zu werden, sind diese Mocks sehr mächtig und daher nicht immer einfach zu bedienen. Dieser Beitrag erklärt die Erstellung eines Mocks durch die Methode „getMock()“.

Mock einer normalen Klasse

Die Signatur der Funktion sieht so aus:

Noch einfach zu verstehen sind „$originalClassName“ und „$mockClassName“. Wir müssen also angeben welche Klasse wir simulieren wollen und wir können optional einen anderen Namen dafür vergeben. Lediglich zu beachten ist, dass für „$originalClassName“ der Klassenname mit vollständigem Namespace angegeben werden muss.

Mit „$methods“ können wir definieren, welche Funktionen simuliert werden sollen. Es besteht also die Möglichkeit nur ein paar Methoden zu verändern und den Rest wie im Original zu lassen. Das ist vor allem dann interessant, wenn wir die zu testende Klasse selbst mocken um interne Verkettungen aufzulösen.
Der Standardwert (leeres Array) besagt allerdings, dass wir alle Funktionen überschreiben wollen, was schon mal zu Verwirrungen führen kann. Wollen wir tatsächlich keine Funktion überschreiben, dann muss hier „null“ übergeben werden. Das passiert zum Beispiel bei Klassen, die einen komplexen Konstruktor haben, den wir per Mock umgehen wollen, die restliche Funktionalität aber erhalten bleiben soll.

Wieder einfacher zu handhaben ist „$arguments“. Hier können die Parameter für den Konstruktor der Klasse übergeben werden. Diese werden dann, wenn „$callOriginalConstructor“ auf true steht, an den Originalkonstruktor übergeben. Diese beiden Parameter gehören somit zusammen.

Eher selten relevant ist „$callOriginalClone“. Damit wir uns darüber Gedanken machen müssen, bedarf es im zu testenden Code eines „clone“ Befehls. Gibt es den, dann können wir mit diesem Parameter entscheiden, ob unser Mock sich einfach nur selbst klont, oder ob es anschließend auch die „__clone()“ der ursprünglichen Klasse aufruft.

Mit „$callAutoload“ können wir entscheiden, ob getMock() den Autoloader benutzen soll um die zu mockende Klasse zu finden. Das ist vergleichbar mit dem zweiten Parameter für „class_exists()“. Der Standardwert „true“ ist fast immer das gewünschte Verhalten. Praxisrelevante Gegenbeispiele sind schwer zu konstruieren und wer tatsächlich mit dem Autoload an dieser Stelle Probleme hat, wird das sehr einfach am FATAL_ERROR merken.

Die beiden letzten Parameter wurden erst in einer neueren PHPUnit-Version hinzugefügt und von den meisten Tutorials gar nicht beachtet. Dementsprechend ist auch ihre Relevanz. „$cloneArguments“ regelt dabei, ob die Parameter bei den Funktionsaufrufen geklont werden sollen. Das ist dann interessant, wenn die ursprüngliche Funktion einen clone Befehl enthält. Wird „$callOriginalMethods“ auf true gesetzt, dann wird die ursprüngliche Funktion weiterhin aufgerufen und wir definieren mit dem Mock nur die Rahmenbedinungen (zum Beispiel wie oft wir erwarten, dass sie aufgerufen wird). Das klingt zwar so, als würden wir uns damit Arbeit ersparen, widerspricht aber dem grundlegenden Testprinzip der sauberen Trennung von Klassen.

Die häufigste Form, mit der ich diese Funktion aufrufe ist:

Für gewöhnlich, will ich alle Methoden kontrollieren und den Konstruktor nicht aufrufen. Nur um sicher zu gehen, schalte ich dann auch noch den Aufruf von __clone() ab.

Mock einer abstrakten Klasse

Ganz ähnlich wie für normale Klassen, gibt es eine „getMock()“ für abstrakte Klassen.

Die Parameter sind genau wie auch für „getMock()“. Wichtigster Unterschied ist, dass alle abstrakten Methoden auf jeden Fall überschrieben werden, egal was wir in „$mockedMethods“ angeben.