Inyección automática de simulacros en sujetos de prueba mediante #InjectMocks y anotaciones #Mock, para acelerar las pruebas unitarias con PHPUnit.
Leia a versão em português aqui.
Para instalar en el entorno de desarrollo:
composer require --dev silasyudi/inject-mocks
Usando las anotaciones #InjectMocks y #Mock en las clases de prueba, puedes inyectar automáticamente simulacros en los sujetos de prueba.
En un escenario típico, lo haríamos así:
class SomeTest extends PHPUnit Framework TestCase
{
public void testSomething()
{
$ someDependency = $ this -> createMock (Dependency::class);
$ anotherDependency = $ this -> createMock (AnotherDependency::class);
. . .
$ subject = new Service ( $ someDependency , $ anotherDependency , ...);
. . .
}
...
Este enfoque trae la dificultad del mantenimiento, porque si se cambia el sujeto de prueba, ya sea agregando, disminuyendo o reemplazando las dependencias, tendrás que cambiarlo en cada prueba.
Con las anotaciones #InjectMocks/#Mock, abstraemos estos cambios del sujeto de prueba. Ejemplo:
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.
}
...
Como en el ejemplo del tema anterior, el atributo #InjectMocks debe colocarse en la propiedad del sujeto de prueba que desea probar, y el atributo #Mock debe colocarse en las propiedades correspondientes a las dependencias que desea simular o inyectar.
Después de eso, ejecute el servicio del inyector con la oración MockInjector::inject($this)
. Esta ejecución se puede declarar en cada prueba o en setUp
.
Después de ejecutar el inyector, el service
anotado con #InjectMocks será una instancia real disponible en el alcance de la clase de prueba, y cada dependencia anotada con #Mock será una instancia de MockObject, inyectada en el sujeto de prueba a través del constructor, y también estará disponible en el alcance de la clase de prueba.
MockInjectException
.#InjectMocks y #Mock funcionan de forma independiente y solos o juntos. Detalles sobre cada uno:
Creará una instancia real a través del constructor y, si hay parámetros en el constructor, se utilizará el siguiente valor en cada parámetro, en este orden:
mock
creado a partir del atributo #[Mock], si existe;default
si es un parámetro opcional;null
si se escribe como null
;mock
si no es un tipo primitivo. En este caso, este mock
no se inyectará en el alcance de TestCase;MockInjectException
.Obs.: Puede utilizar el atributo #Mock en todas, algunas o ninguna de las dependencias del sujeto de prueba.
Creará un mock
inyectado en el alcance de TestCase, sin utilizar el constructor. Este comportamiento de creación es idéntico a TestCase::createMock()
.