Injeção automática de mocks em assuntos de teste por meio de anotações #InjectMocks e #Mock, para acelerar o teste de unidade com PHPUnit.
Leia a versão em português aqui.
Para instalar no ambiente de desenvolvimento:
composer require --dev silasyudi/inject-mocks
Usando anotações #InjectMocks e #Mock em classes de teste, você pode injetar simulações automaticamente em assuntos de teste.
Em um cenário típico, faríamos assim:
class SomeTest extends PHPUnit Framework TestCase
{
public void testSomething()
{
$ someDependency = $ this -> createMock (Dependency::class);
$ anotherDependency = $ this -> createMock (AnotherDependency::class);
. . .
$ subject = new Service ( $ someDependency , $ anotherDependency , ...);
. . .
}
...
Essa abordagem traz a dificuldade de manutenção, pois se o sujeito do teste for alterado, seja adicionando, diminuindo ou substituindo as dependências, você terá que alterá-lo a cada teste.
Com as anotações #InjectMocks/#Mock, abstraímos essas alterações nas cobaias de teste. Exemplo:
use SilasYudi InjectMocks InjectMocks ;
use SilasYudi InjectMocks Mock ;
use SilasYudi InjectMocks MockInjector ;
class SomeTest extends PHPUnit Framework TestCase
{
#[Mock]
private Dependency $ someDependency ;
#[Mock]
private AnotherDependency $ anotherDependency ;
. . .
# [InjectMocks]
public function setUp() : void
{
MockInjector:: inject ( $ this );
}
public void testSomething ()
{
// $this->subject e as dependências já estão instanciadas.
}
...
Assim como no exemplo do tópico anterior, o atributo #InjectMocks deve ser colocado na propriedade do sujeito de teste que você deseja testar, e o atributo #Mock deve ser colocado nas propriedades correspondentes às dependências que você deseja simular ou injetar.
Depois disso, execute o serviço injetor com a frase MockInjector::inject($this)
. Esta execução pode ser declarada em cada teste ou em setUp
.
Após a execução do injetor, o service
anotado com #InjectMocks será uma instância real disponível no escopo da classe de teste, e cada dependência anotada com #Mock será uma instância de MockObject, injetada no objeto de teste através do construtor, e será também estará disponível no escopo da classe de teste.
MockInjectException
.#InjectMocks e #Mock funcionam de forma independente e isolada ou em conjunto. Detalhes sobre cada um:
Irá criar uma instância real através do construtor, e caso existam parâmetros no construtor, será utilizado o seguinte valor em cada parâmetro, nesta ordem:
mock
criada a partir do atributo #[Mock], se existir;default
se for um parâmetro opcional;null
se for digitado como null
;mock
se não for um tipo primitivo. Neste caso, este mock
não será injetado no escopo TestCase;MockInjectException
.Obs.: Você pode usar o atributo #Mock em todas, algumas ou nenhuma dependência da cobaia.
Irá criar um mock
injetado no escopo TestCase, sem usar o construtor. Este comportamento de criação é idêntico a TestCase::createMock()
.