Automatische simulierte Abhängigkeitsinjektion zum Testen
injected Trait
können Sie auf einfache Weise Klassen erstellen, deren Abhängigkeiten zu Testzwecken simuliert werden.
Das folgende Muster ist sehr häufig:
A
mit ihren Abhängigkeiten (Dienstobjekten), die über den Konstruktor übergeben werdenA
nachahmen und sicherstellen, dass sie wie erwartet aufgerufen werden. Ein Großteil der Testlogik dient letztlich als Vorlage für die Konstruktion eines Objekts. Das Ziel injected Trait
besteht darin, diese Grundbausteine vollständig zu entfernen, sodass Sie sich auf das Wesentliche konzentrieren können: die Tests selbst.
Verwenden Sie composer
, um die neueste Version zu erhalten (Sie benötigen sie wahrscheinlich nur für die Entwicklung):
$ composer require sellerlabs/ injected --dev
Angenommen, wir entwickeln eine Web-App und möchten den Benutzern eine E-Mail senden, wenn sie sich anmelden. Nehmen wir als einfaches Beispiel an, dass ein Benutzer vollständig durch seine E-Mail-Adresse definiert wird. Wenn sie sich anmelden, möchten wir ihnen natürlich eine Dankes-E-Mail senden. Darüber hinaus möchten wir testen, ob die E-Mails tatsächlich versendet werden , ohne sie tatsächlich zu versenden .
Definieren wir zunächst einen E-Mail-Dienst:
class EmailService
{
public function email ( $ address , $ content )
{
// Send an email to $address with body $content
}
}
(In einer echten Anwendung würde email
eine E-Mail verschicken – die Implementierung geht uns hier allerdings nicht an!)
Definieren wir außerdem einen UserController
, der den äußerst einfachen Anmeldevorgang abwickelt:
class UserController
{
private $ service ;
public function __construct ( EmailService $ service )
{
$ this -> service = $ service ;
}
public function signUp ( $ emailAddress )
{
$ this -> service -> email ( $ emailAddress , ' Thanks for signing up! ' );
return $ emailAddress ;
}
}
Hier stellen wir die EmailService
Abhängigkeit über den Konstruktor bereit und verwenden sie während unseres (unglaublich einfachen) Anmeldevorgangs.
Um diese Klasse zu testen, müssen wir eines von zwei Dingen tun:
EmailService
Objekt mit etwas wie Mockery
und stellen Sie sicher, dass email
mit den erwarteten Argumenten aufgerufen wird. injected Trait
ermöglicht es Ihnen, Option 2 schmerzlos zu erreichen. Werfen wir einen Blick darauf:
use SellerLabs injected injected Trait ;
/**
* Class injected Example
*
* // 1. These are helpful annotations for IDEs and language tools
* @property MockInterface $service
* @method UserController make()
*
* @author Benjamin Kovach <[email protected]>
*/
class injected Example extends PHPUnit_Framework_TestCase
{
// 2. Use our trait
use injected Trait;
// 3. Provide the name of the class to test
protected $ className = UserController::class;
public function testSignUp ()
{
// 4. Make a controller with mocked dependencies
$ controller = $ this -> make ();
$ address = ' [email protected] ' ;
// 5. We can access any mocked dependency of the class as a property
$ this -> service -> shouldReceive ( ' email ' )
-> withArgs (
[
$ address ,
' Thanks for signing up! '
]
);
$ result = $ controller -> signUp ( $ address );
$ this -> assertEquals ( $ address , $ result );
}
}
Jede Klasse, die injected Trait
verwendet, muss über die Eigenschaft $className
verfügen, die zum Auffinden der getesteten Klasse verwendet wird. injected Trait
stellt eine einzige öffentliche Methode bereit, make
, die ein Objekt dieses Typs erstellt, seine Abhängigkeiten jedoch verspottet und sie als Eigenschaften in der Testklasse selbst speichert.
In testSignUp
erstellen wir also den Controller mit make()
, was uns Zugriff auf ein simuliertes Objekt vom Typ EmailService
namens $service
gibt. Dies liegt daran, dass es im Konstruktor des UserController
so definiert ist:
public function __construct ( EmailService $ service )
{
$ this -> service = $ service ;
}
Für die Dauer des Testfalls ist die Mitgliedsvariable $service
an diesen simulierten EmailService
gebunden, was es uns ermöglicht, Erwartungen darüber zu treffen, was damit passiert, wenn die signUp
-Methode des Controllers aufgerufen wird. Wir verwenden Mockery
, um die verspotteten Objekte zu erstellen. Es gibt einige Anmerkungen im Klassenkommentar, die bei der automatischen IDE-Vervollständigung für diese Klassen helfen, da die Scheineigenschaften dynamisch deklariert werden.
Dieses Beispiel befindet sich in tests/ injected Example.php
. Schauen Sie sich ruhig um!
Die Auswirkung dieser Eigenschaft mag relativ gering erscheinen, aber bei der Arbeit an großen Anwendungen, in denen Klassen mehrere Abhängigkeiten haben, erleichtert dies das Testen erheblich .