Injeção automática de dependência simulada para teste
injected Trait
permite que você crie facilmente classes com todas as suas dependências simuladas para fins de teste.
O seguinte padrão é muito comum:
A
com suas dependências (objetos de serviço) passadas pelo construtorA
, afirmando que elas são chamadas conforme o esperado. Grande parte da lógica de teste acaba sendo padrão para a construção de um objeto. injected Trait
visa remover totalmente esse padrão, permitindo que você se concentre no que importa: os próprios testes.
Use composer
para obter a versão mais recente (provavelmente você só precisará dele para desenvolvimento):
$ composer require sellerlabs/ injected --dev
Suponha que estejamos desenvolvendo um aplicativo da web e queiramos enviar um e-mail aos usuários quando eles se inscreverem. Para um exemplo simples, vamos supor que um usuário seja definido inteiramente por seu endereço de e-mail. Quando eles se inscrevem, naturalmente queremos enviar-lhes um e-mail de agradecimento. Além disso, gostaríamos de testar se os e-mails estão realmente sendo enviados sem realmente enviá-los .
Primeiro, vamos definir um serviço de e-mail:
class EmailService
{
public function email ( $ address , $ content )
{
// Send an email to $address with body $content
}
}
(Em uma aplicação real, email
enviaria um e-mail - no entanto, não estamos preocupados com a implementação aqui!)
Vamos também definir um UserController
que lida com o processo de inscrição extremamente simples:
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 ;
}
}
Aqui, fornecemos a dependência EmailService
por meio do construtor e a usamos durante nosso processo de inscrição (incrivelmente simples).
Para testar esta classe, teremos que fazer uma de duas coisas:
EmailService
usando algo como Mockery
e certifique-se de que email
seja chamado com os argumentos esperados. injected Trait
permite que você alcance a opção 2 sem dor. Vamos dar uma olhada:
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 );
}
}
Toda classe que usa injected Trait
deve ter a propriedade $className
, que é usada para localizar a classe que está sendo testada. injected Trait
fornece um único método público, make
, que constrói um objeto desse tipo, mas zomba de suas dependências e as salva como propriedades na própria classe de teste.
Portanto, em testSignUp
, estamos construindo o controlador usando make()
, o que nos dá acesso a um objeto do tipo EmailService
simulado chamado $service
. Isso ocorre porque é definido dessa forma no construtor do UserController
:
public function __construct ( EmailService $ service )
{
$ this -> service = $ service ;
}
Durante o caso de teste, a variável de membro $service
está vinculada a esse EmailService
simulado, o que nos permite fazer expectativas sobre o que acontece com ele quando o método signUp
do controlador é chamado. Usamos Mockery
para criar os objetos simulados. Existem algumas anotações no comentário da classe, que ajudam no preenchimento automático do IDE para essas classes, uma vez que as propriedades simuladas são declaradas dinamicamente.
Este exemplo reside em tests/ injected Example.php
. Sinta-se à vontade para fuçar!
O impacto dessa característica pode parecer relativamente pequeno, mas ao trabalhar em aplicações grandes onde as classes têm diversas dependências, isso torna o teste muito mais fácil.