Auch wenn es noch keine ausführliche Beschreibung vom Unittesting gab, möchte ich das Mocking Framework Mockery vorstellen. Ein Mocking Framework nutzt man, um Mock-Objekte zu erstellen. In phpUnit ist solch ein Framework bereits integriert. Mockery ist davon aber getrennt und ist auch etwas anders nutzbar. Ein Mock-Objekt ist nun eine Attrappe oder auch „Dummy“-Objekt. Diese Objekte nutzt man, wenn man beim Testen einer Anwendung bestimmte Objekte nicht erzeugen will oder kann.
Der Vorteil eines Mocking Frameworks ist bspw auch, dass man nur ein Interface benötigt, um eine passende Attrappe zu erstellen. Dies werden wir einmal ausprobieren. Hierzu definiere ich ein Interface, dass einige mathematischen Funktionen bereitstellt. Es ist nicht unbedingt das schönste Interface, aber für einen Test ist es vollkommen ausreichend.
interface Math {
public function add($x,$y);
public function sub($x,$y);
public function mult($x,$y);
public function div($x,$y);
public function random();
public function fixed();
}
Die Methoden add, sub, mult und div stehen für addieren, subtrahieren, multiplizieren und dividieren. Es werden jeweils 2 Werte angegeben, die dann verrechnet werden. Wie ist mir natürlich vollkommen unbekannt. Das ist Aufgabe desjenigen, der eine Klasse implementiert für dieses Interface. random und fixed geben einen zufälligen Wert bzw. eine Konstante (oder einen fixen Wert) zurück.
Bisher nicht sonderlich spektakulär. Aber wir wollen nun ein Objekt erzeugen, dass es eigentlich nicht gibt. Mockery eilt zur Hilfe und ist mE sehr schön zu benutzen, da es ein fluent Interface anbietet.
Zuerst müssen das Interface und die Framework-Klasse inkludiert werden, damit PHP weiß, wovon wir überhaupt reden. In einem echten Projekt wäre ein schöner Autoloader sicher die bessere Wahl, aber für dieses Proof of Concept arbeiten wir mit Nun kommen wir zum spannenden Teil. Wir erzeugen uns eine Variable, die sich verhält, als ob sie eine Instanz einer Klasse wäre, die Math implementiert. Jetzt testen wir mal unser neues Objekt, ob es sich wie ein normales Objekt verhält. Zuerst überprüfen wir, ob das Objekt wirklich das Math-Interface implementiert. Dann lassen wir uns einen Zufallswert ausgeben. Nach der Addition von 2 und 3 gibt es noch einen Zufallswert. Dann gibt es einen fixen Wert ohne Angabe eines Parameters. Und nach dem dritten Zufallswert bekommen wir noch den fixen Wert zu 23. Nun kann jeder überlegen was man hier erwarten würde als Ausgabe. Und dasrauf könnte man nun einen UnitTest aufbauen. Die Ausgabe möchte ich natürlich nicht vorenthalten und man erhält somit: Natürlich kann man die Mockobjekte noch komplexer und raffinierter ausbauen und dazu gibt es auf der Mockery-Seite einige Infos. Den Download findet man auf GitHub.include
include "Math.php";
include "../library/Mockery/Framework.php";
$calculator = Mockery::mock("Math");
$calculator->shouldReceive("random")->times(3)->andReturn(10,12,14);
$calculator->shouldReceive("add")->with(2,3)->andReturn(5);
$calculator->shouldReceive("fixed")->withAnyArgs()->andReturn(7);
$calculator
implementiert nun Math. Und es gibt die Methoden random, add und fixed. Die anderen könnte man auch "mocken", aber das trägt nicht zum Verständnis bei. Was man nun sehen kann ist, dass random 3 mal (times) aufrufbar ist und dabei 10, 12 und 14 zurückgeben wird. Nicht wirklich zufällig aber für einen möglichen Test zufällig genug.
add
kann man mit den Parametern 2 und 3 aufrufen und man erhält erwartungsgemäß 5 zurück. Der fixe Wert ist 7 und diesen erhält man bei beliebiger Eingabe.
echo "Instance of Math: ".(($calculator instanceof Math) ? "true" : "false")."\n";
echo "Random 1: ".$calculator->random()."\n";
echo "Add 1: ".$calculator->add(2,3)."\n";
echo "Random 2: ".$calculator->random()."\n";
echo "Fixed 1: ".$calculator->fixed()."\n";
echo "Random 3: ".$calculator->random()."\n";
echo "Fixed 2: ".$calculator->fixed(23)."\n";
Instance of Math: true
Random 1: 10
Add 1: 5
Random 2: 12
Fixed 1: 7
Random 3: 14
Fixed 2: 7