Mocks und Stubs mit Mockery

Heute möchte ich euch das PHP-Framework “Mockery” vorstellen. Wie dessen Name und der Titel dieses Blog-Post schon vermuten lassen, ist es damit möglich Mocks und Stubs (im Folgenden nur noch “Mock”) in/für PHP zu erstellen. Mockery arbeitet prinzipiell wunderbar mit z.B. PHPUnit zusammen und kollidiert nicht mit anderen Frameworks, jedoch gibt es gerade in dieser Kombination, für einen erfolgreichen Einsatz, einiges zu beachten.
Mit Mockery können fünf verschiedene Arten von Mocks erstellt werden. Die einfachste Art sind Mocks ohne Bezug zu jeglichen bereits existierenden Code-Bestandteilen. Mocks können jedoch auch von einem bestimmten Typ sein, sei es nun eine konkrete bzw. abstrakte Klasse oder ein Interface. Da Mocks standardmäßig alle Methoden einer Klasse maskieren, kann man sie als “partielle” Mocks deklarieren wodurch nur ganz bestimmte Methoden maskiert werden. Statische Methoden und sogar Instanziierungen können mit Hilfe von “Alias-Mocks” gemocked werden, sofern auto loading genutzt wird und die eigentlichen Klassen noch nicht geladen wurden. Selbst bereits existierende Mocks können erweitert werden und erben so alle Eigenschaften des Basis-Mocks.
Damit Mocks aber überhaupt erst sinnvoll verwendet werden können, muss definiert werden wie diese verwendet werden. Dies geschieht mit sogenannten “Expectations”. Eine solche Expectation besteht aus mindestens einem Methoden-Namen und optional ein oder mehreren Argument-Validatoren. Diese Validatoren bestimmen wie die Argumente, die die Methode erwartet, auszusehen haben. Entspricht etwas nicht den Erwartungen wird eine Ausnahme geworfen, was im Falle von PHPUnit zu einem fehlgeschlagenen Test führt.
Selbstverständlich kann man auch das Verhalten eines Mocks, mit sogenannten “Behaviour modifiers”, beeinflussen. Ob ein bestimmter Wert zurückgegeben, eine bestimmte Ausnahme geworfen, eine bestimmte Eigenschaft gesetzt oder die Methode nur eine bestimmte Anzahl oft (Nie?) aufgerufen werden soll, alles kein Problem für Mockery.
Ein besonders tolles Feature von Mockery, sind die “Demeter Chains”. Hiermit lassen sich fluid interfaces auf sehr einfache Art und Weise mocken:

$fritz = Mockery::mock(‘FischersFritz’);
$fritz->shouldReceive(‘fischt->frische->fische’);
$fritz->fischt()->frische()->fische();

Zu beachten ist hier, dass diese Form der Expectation nicht kompatibel mit der Methode getMock() ist:

// getMock() gibt nicht den Mock zurück. Dies ist leider kein bug.
$fritz = Mockery::mock(‘SomeFritz’)->shouldReceive(‘fischt->frische->fische’)->getMock();

Mockery hat jedoch auch seine Kehrseiten. So ist es z.B. inkompatibel mit dem Feature “backupStaticAttributes” von PHPUnit, kann keine Mocks mit Zuständen und keine multiplen “Demeter Chains” abbilden. Außerdem ist man z.T. gezwungen das “runInSeparateProcess” Feature von PHPUnit zu verwenden, wenn man “Alias-Mocks” einsetzt.
Im Großen und Ganzen bin ich sehr zufrieden mit Mockery, setze es gerne ein und kann es nur weiter empfehlen. Da ich allerdings nicht die vollständige Funktionalität von Mockery vorgestellt habe, lege ich jedem ans Herz einen Blick in die doch sehr umfangreiche und übersichtliche Dokumentation zu werfen.

Johannes Meyer
Johannes Meyer
Developer

Johannes ist seit 2011 bei uns und hilft bei der Entwicklung zukünftiger Knüller (Icinga2, Icinga Web 2, ...) aus dem Hause NETWAYS.