Mockery es una biblioteca de PHP que permite crear mocks (objetos simulados) para realizar pruebas unitarias. Un mock es un objeto que simula el comportamiento de otro objeto real en un entorno controlado. Esto es particularmente útil cuando se necesita aislar la lógica que se desea probar de sus dependencias externas, como bases de datos, servicios externos, o incluso otras clases dentro de la misma aplicación.
1. Aislamiento de pruebas: Al usar mocks, se pueden aislar las pruebas unitarias para asegurarse de que solo se está probando la lógica específica de la clase o método en cuestión.
2. Simulación de dependencias complejas: Mockery permite simular dependencias complicadas o difíciles de configurar, como servicios externos o bases de datos.
3. Control de comportamiento: Con Mockery, puedes definir cómo deben comportarse los mocks en diferentes escenarios, incluyendo excepciones y respuestas específicas.
Antes de poder utilizar Mockery en tus tests de Laravel, necesitas asegurarte de que la biblioteca esté instalada en tu proyecto. Puedes instalar Mockery mediante Composer:
composer require --dev mockery/mockery
Esta instrucción añade Mockery como una dependencia de desarrollo en tu proyecto. Dado que Laravel ya integra PHPUnit, no es necesario instalarlo por separado.
Una vez instalada, Mockery puede ser utilizado directamente en tus tests. Sin embargo, es recomendable configurarlo para que se integre bien con PHPUnit, lo que facilita su uso y gestión de mocks en cada prueba.
Abre el archivo phpunit.xml
en la raíz de tu proyecto Laravel y asegúrate de que Mockery se utiliza correctamente al finalizar cada test:
<php>
<env name="APP_ENV" value="testing"/>
<env name="CACHE_DRIVER" value="array"/>
<env name="SESSION_DRIVER" value="array"/>
<env name="QUEUE_DRIVER" value="sync"/>
<env name="MAIL_MAILER" value="array"/>
<env name="TELESCOPE_ENABLED" value="false"/>
<env name="LOG_CHANNEL" value="errorlog"/>
</php>
A continuación, vamos a examinar cómo puedes utilizar Mockery en algunos ejemplos prácticos.
Imagina que tienes un servicio que depende de una clase PaymentGateway
para procesar pagos. Quieres probar un método processPayment
en tu clase OrderService
, pero no deseas realizar una transacción real durante la prueba. Aquí es donde Mockery resulta útil.
use Mockery;
use PHPUnit\Framework\TestCase;
class OrderServiceTest extends TestCase
{
public function testProcessPayment()
{
// Crear un mock de PaymentGateway
$paymentGatewayMock = Mockery::mock(PaymentGateway::class);
// Configurar el mock para que el método `charge` devuelva true
$paymentGatewayMock->shouldReceive('charge')
->once()
->with(1000)
->andReturn(true);
// Inyectar el mock en OrderService
$orderService = new OrderService($paymentGatewayMock);
// Ejecutar el método a probar
$result = $orderService->processPayment(1000);
// Verificar el resultado
$this->assertTrue($result);
}
// Destruir los mocks después de cada test
public function tearDown(): void
{
Mockery::close();
parent::tearDown();
}
}
1. Crear un Mock: Utilizamos Mockery::mock(PaymentGateway::class)
para crear un mock de la clase PaymentGateway
.
2. Configurar el Mock: Definimos que el método charge
será llamado una vez con un argumento de 1000, y devolverá true
.
3. Inyectar el Mock: Inyectamos el mock en OrderService
, lo que nos permite simular el comportamiento de PaymentGateway
sin realizar una transacción real.
4. Ejecutar y Verificar: Ejecutamos el método processPayment
y verificamos que el resultado sea true
.
5. Cerrar Mockery: Al final de cada test, llamamos a Mockery::close()
para asegurar que no haya mocks residuales.
Supongamos que el método charge
puede lanzar una excepción en caso de error. Queremos probar que nuestro OrderService
maneja correctamente esta excepción.
use Mockery;
use PHPUnit\Framework\TestCase;
class OrderServiceTest extends TestCase
{
public function testProcessPaymentHandlesException()
{
// Crear un mock de PaymentGateway
$paymentGatewayMock = Mockery::mock(PaymentGateway::class);
// Configurar el mock para que lance una excepción
$paymentGatewayMock->shouldReceive('charge')
->once()
->with(1000)
->andThrow(new PaymentFailedException);
// Inyectar el mock en OrderService
$orderService = new OrderService($paymentGatewayMock);
// Ejecutar el método a probar y verificar que la excepción es manejada
$result = $orderService->processPayment(1000);
$this->assertFalse($result);
}
public function tearDown(): void
{
Mockery::close();
parent::tearDown();
}
}
1. Lanzar una Excepción: Usamos andThrow
para indicar que el método charge
debe lanzar una PaymentFailedException
cuando sea llamado.
2. Manejo de la Excepción: En nuestro test, verificamos que processPayment
devuelve false
cuando se lanza la excepción.
1. Cerrar Mockery: Siempre cierra Mockery después de cada test utilizando Mockery::close()
. Esto asegura que no haya efectos secundarios no deseados.
2. Mocks Específicos: Crea mocks solo para las dependencias que realmente necesitas simular. Evita crear mocks de todo, ya que esto puede llevar a pruebas menos fiables.
3. Verificar Interacciones: Utiliza shouldReceive()
y once()
para verificar que los métodos se llamen el número correcto de veces y con los argumentos adecuados.
Mockery es una herramienta poderosa que facilita la creación de pruebas unitarias en Laravel al permitir la simulación de dependencias complejas. Al dominar su uso, puedes aislar mejor la lógica de tu aplicación y asegurar que tus pruebas sean más precisas y confiables.
Utilizar Mockery en conjunto con Laravel y PHPUnit no solo mejora la calidad de tus pruebas, sino que también te permite desarrollar con mayor confianza, sabiendo que tu código ha sido probado rigurosamente en un entorno controlado. ¡Empieza a implementar Mockery en tus pruebas hoy mismo y lleva la calidad de tu código al siguiente nivel! 😃🙂
Jorge García
Fullstack developer