Mocks y Fixtures en Pruebas PHP: Domina los Test Doubles

Entrenador de Python en Línea para Principiantes

Aprende Python fácilmente sin sobrecargarte de teoría. Resuelve tareas prácticas con verificación automática, recibe pistas y escribe código directamente en el navegador, sin instalar nada.

Start Course

Por qué las pruebas sin mocks son como cocinar sin sal

\n\n

Imagina que estás desarrollando una aplicación que envía notificaciones por email, trabaja con una base de datos y consume una API externa. ¿Cómo probarías esto sin mocks y fixtures en PHP? Tendrías que enviar correos reales cada vez, crear registros de prueba en la BD y esperar la respuesta del servicio externo. Es lento, poco fiable y, francamente, una mala práctica.

\n\n

Aquí es donde entran los test doubles — sustitutos de objetos reales. En el ecosistema PHP, la combinación ganadora es: mocks para verificar interacciones y fixtures para preparar los datos. Veamos cómo funciona esto en la práctica.

\n\n

Mock vs Stub vs Fake: no los confundas

\n\n

Antes de meternos en código, aclaremos conceptos. En el mundo de las pruebas unitarias en PHP, existen tres tipos principales de sustitutos:

\n\n
    \n
  • Stub — devuelve datos predefinidos. Verificas que tu código procesa esos datos correctamente.
  • \n
  • Mock — no solo devuelve datos, sino que también verifica cómo tu sistema llama a los métodos: cuántas veces, con qué argumentos y en qué orden.
  • \n
  • Fake — una implementación simplificada pero funcional (por ejemplo, una base de datos en memoria).
  • \n
\n\n

En PHPUnit, el estándar de facto para testing en PHP, estos conceptos a menudo se mezclan. Pero seamos precisos: los mocks en PHP son aquellos donde defines expectativas (expectations).

\n\n

Escribiendo tu primer mock con PHPUnit

\n\n

Supongamos que tenemos una clase Mailer que envía correos. Queremos verificar que el servicio de registro llama al método send() exactamente una vez con los datos correctos.

\n\n
<?php\nuse PHPUnit\\Framework\\TestCase;\n\nclass UserRegistrationTest extends TestCase\n{\n    public function testSendWelcomeEmailOnRegistration(): void\n    {\n        // Creamos un mock para Mailer\n        $mailer = $this->createMock(Mailer::class);\n        \n        // Configuramos la expectativa: el método send se llamará 1 vez\n        $mailer->expects($this->once())\n               ->method('send')\n               ->with(\n                   $this->equalTo('user@example.com'),\n                   $this->stringContains('Bienvenido')\n               );\n        \n        $service = new UserRegistrationService($mailer);\n        $service->register('user@example.com', 'password123');\n    }\n}\n
\n\n

Observa: no verificamos que el correo se haya enviado realmente. Verificamos que nuestro código intentó enviarlo — esa es la esencia de los mocks en pruebas PHP. Si el método send() no se llama (o se llama dos veces), el test fallará.

\n\n

El método expects y sus posibilidades

\n\n

PHPUnit ofrece una API completa para configurar expectativas:

\n\n
    \n
  • $this->once() — exactamente una llamada
  • \n
  • $this->exactly(3) — exactamente tres llamadas
  • \n
  • $this->atLeastOnce() — al menos una llamada
  • \n
  • $this->never() — el método no debe ser llamado
  • \n
\n\n

Para los argumentos, usa matchers (restricciones):

\n\n
$mailer->expects($this->once())\n       ->method('send')\n       ->with(\n           $this->callback(function($email) {\n               return filter_var($email, FILTER_VALIDATE_EMAIL) !== false;\n           }),\n           $this->anything() // el segundo argumento no nos importa\n       );\n
\n\n

Fixtures en PHP: prepara los datos antes de que los necesites

\n\n

Los fixtures son el estado predefinido de los datos en el que se encuentra el sistema antes de ejecutar una prueba. En PHPUnit, se utilizan los métodos setUp() y

Blogs

Book Recommendations