Inyección automática de dependencia simulada para pruebas
injected Trait
le permite crear fácilmente clases con todas sus dependencias simuladas para fines de prueba.
El siguiente patrón es muy común:
A
con sus dependencias (objetos de servicio) pasadas a través del constructorA
, afirmando que se llaman como se esperaba. Gran parte de la lógica de prueba termina siendo un modelo repetitivo para construir un objeto. injected Trait
tiene como objetivo eliminar este texto estándar por completo, permitiéndole concentrarse en lo que importa: las pruebas en sí.
Utilice composer
para obtener la última versión (probablemente solo la necesite para el desarrollo):
$ composer require sellerlabs/ injected --dev
Supongamos que estamos desarrollando una aplicación web y queremos enviar un correo electrónico a los usuarios cuando se registran. Para poner un ejemplo sencillo, supongamos que un usuario está definido completamente por su dirección de correo electrónico. Cuando se registran, naturalmente queremos enviarles un correo electrónico de agradecimiento. Además, nos gustaría probar que los correos electrónicos realmente se envían sin enviarlos .
Primero, definamos un servicio de correo electrónico:
class EmailService
{
public function email ( $ address , $ content )
{
// Send an email to $address with body $content
}
}
(En una aplicación real, email
enviaría un correo electrónico; ¡aunque aquí no nos preocupa la implementación!)
También definamos un UserController
que maneje el proceso de registro extremadamente simple:
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 ;
}
}
Aquí, proporcionamos la dependencia EmailService
a través del constructor y la usamos durante nuestro (increíblemente simple) proceso de registro.
Para probar esta clase, tendremos que hacer una de dos cosas:
EmailService
usando algo como Mockery
y asegúrese de que se llame email
con los argumentos esperados. injected Trait
le permite lograr la opción 2 sin dolor. Echemos un vistazo:
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 );
}
}
Cada clase que utiliza injected Trait
debe tener la propiedad $className
, que se utiliza para localizar la clase que se está probando. injected Trait
proporciona un único método público, make
, que construye un objeto de este tipo, pero se burla de sus dependencias y las guarda como propiedades en la propia clase de prueba.
Entonces, en testSignUp
, estamos construyendo el controlador usando make()
, lo que nos da acceso a un objeto de tipo EmailService
simulado llamado $service
. Esto se debe a que está definido de esa manera en el constructor de UserController
:
public function __construct ( EmailService $ service )
{
$ this -> service = $ service ;
}
Mientras dure el caso de prueba, la variable miembro $service
está vinculada a este EmailService
simulado, lo que nos permite crear expectativas sobre lo que sucede con él cuando se llama al método signUp
del controlador. Usamos Mockery
para crear los objetos simulados. Hay algunas anotaciones en el comentario de la clase que ayudan con el autocompletado IDE para estas clases, ya que las propiedades simuladas se declaran dinámicamente.
Este ejemplo se encuentra en tests/ injected Example.php
. ¡Siéntete libre de husmear!
El impacto de esta característica puede parecer relativamente pequeño, pero cuando se trabaja en aplicaciones grandes donde las clases tienen varias dependencias, esto facilita mucho las pruebas.