https://a.storyblok.com/f/270183/1368x665/24a2391523/26mar_dev-blog_php-pest-mut-test.jpg

Potencie su código PHP con las pruebas de mutación PEST

Publicado el March 3, 2026

Tiempo de lectura: 5 minutos

PEST ha visto una cantidad decente de adopción, especialmente desde que se ha convertido en la biblioteca de pruebas de facto para Laravel dando la opción a los desarrolladores de Laravel de elegir entre PHPUnit o PEST, ejecutando PHPUnit bajo el capó. Más opciones sólo puede ser una buena cosa, pero las tasas de adopción también significa un ciclo de vida de desarrollo que ha visto un montón de nuevas funcionalidades nuevas características.

El ritmo al que se pusieron de moda las pruebas de mutación fue sorprendente, dado que no había pasado gran cosa en el mundo de las pruebas desde Diseño orientado al dominio y desarrollo orientado al comportamiento. Recuerdo que me quedé alucinado con una demostración de BDD que analizaba automáticamente Gherkin y escupiendo pruebas de él por Ciaran McNulty en PHP Londres hace años. Es algo que nunca he hecho profesionalmente, pero una vez que las pruebas de mutación empezaron a ser adoptadas por los desarrolladores de PHP tras la popularidad de InfectionPHPsupe que sería algo en lo que vería un gran valor.

Y así, con las pruebas de mutaciones cada vez más populares, Nuno Maduro anunció que PEST 3.0 incluiría pruebas de mutaciones. En este artículo, vamos a sumergirnos en ello con una prueba de ejemplo utilizando la API de mensajería de Vonage.

¿Qué son las pruebas de mutaciones y por qué se utilizan?

Permíteme empezar diciendo que cualquier herramienta que puedas añadir para que tu código sea lo más robusto posible siempre va a ser positiva. Por lo tanto, puedes añadir a la lista de esenciales, como Análisis Estático con los gustos de SalmoPHP o PHPStanlinting con algo como phpcsfixery pruebas robustas con Test-Driven Development.

Somos humanos y, como tales, cometemos errores en nuestras pruebas (sé que yo los he cometido). Hay muchas cosas que pueden salir mal: imagina que estás escribiendo una prueba y no tiene ninguna aserción, sólo quieres comprobar que el código se ha ejecutado sin lanzar una excepción.

public function testService()
{
	$service = new \Vonage\ServiceContainer('dev');
	$service->runService();

	// If service fails, exception, so dummy completion assertion
	$this->assertTrue(true);
}

Lo que tienes ahí es un falso positivo. Claro, se puede ver el razonamiento detrás de él, pero también tiene

100% Line Test Coverage

Técnicamente es correcto, pero no es una prueba real. ¿Qué pasa si el código subyacente cambia de tal manera que la excepción no documentada que podría lanzarse ya no puede ocurrir?

Lo que hacen las pruebas de mutación es cambiar el código subyacente, un hilo cada vez (a estos hilos los llamamos "mutantes"). Si empiezas a modificar el código, técnicamente, todas tus pruebas deberían fallar. Ya hemos introducido el elemento humano en ello, donde sabemos que podría no ser el caso. El resultado final que buscamos es que se hayan creado muchísimos mutantes, pero que todos ellos hayan "muerto", es decir, que la prueba haya fallado a causa de la mutación. Si hay mutantes que "sobreviven", las pruebas siguen pasando. Si siguen pasando, significa que las pruebas no han notado ningún cambio en tu código. Eso no es bueno.

Nuestra línea de base: Prueba del SDK PHP de Vonage

Vamos a mostrar cómo implementamos una prueba regular primero, teniendo un servicio imaginario de Laravel que envía un Mensaje RCS usando Vonage.

<?php

namespace App\Services;

use Vonage\Client;

use Vonage\Messages\Channel\RCS\RcsText;

class RcsService
{

   public function __construct(
       private Client $vonage
   ) {}

   public function send(string $to, string $message): array
   {
       $response = $this->vonage->messages()->send(
           new RcsText($to, 'VonageApp', $message)
       );

       return $response;
   }
}

Para simplificar, diremos que el contenedor de servicios de Laravel inyecta un objeto Vonage Client preconfigurado con credenciales en el constructor. Ahora necesitamos una prueba para ello que en realidad no hace una llamada a la API. Podemos hacerlo usando Mocks.

use App\Services\RcsService;
use Vonage\Client;
use Vonage\Messages\Channel\RCS\RcsText;
use Vonage\Messages\Client as MessagesClient;

beforeEach(function () {
   $this->messagesClient = Mockery::mock(MessagesClient::class);
   $this->vonage = Mockery::mock(Client::class);
   $this->vonage->shouldReceive('messages')->andReturn($this->messagesClient);
});

afterEach(function () {
   Mockery::close();
});

test('Will send message using Vonage SDK to end number', function () {
   $response = ['message_uuid' => 'abc-123', 'to' => '33600000000'];

   $this->messagesClient
       ->shouldReceive('send')
       ->once()
       ->with(Mockery::type(RcsText::class))
       ->andReturn($response);

   $service = new RcsService($this->vonage);
   $result = $service->send('+33600000000', 'Hello world');
   expect($result)->toBeArray();
});

OK, bien, la prueba pasará. Bien, bien. Pero, ¿qué pasa si mutamos el código?

Las pruebas de mutación están integradas en PEST 3, lo que significa que podemos ejecutar mutaciones en este momento y ver qué pasa. Hay un montón de opciones de configuración para PEST, pero la forma más sencilla de instruir a las mutaciones que se produzcan es el uso de la función coberturas() método de ayuda. En este caso, la siguiente línea se añade al archivo de prueba:

use App\Services\RcsService;

covers(RcsService::class);

Esto le dice a PEST que cuando ejecute este archivo de prueba, siga adelante y mute el código en RcsService. Para ejecutar las mutaciones de PEST con un umbral de aprobación del 100%:

./vendor/bin/pest --mutate --min=100

Obtenemos el siguiente resultado:

 Mutating application files...

  1 Mutations for 1 Files created

   RUN  app/Services/RcsService.php

  ⨯ Line 20: AlwaysReturnEmptyArray

  ---------------------------------------------------------------------------------------------------------------------------  

   UNTESTED  app/Services/RcsService.php  > Line 20: AlwaysReturnEmptyArray - ID: 5befe24d3b8d7f6f

       public function send(string $to, string $message): array

       {

           $response = $this->vonage->messages()->send(new RcsText($to, 'VonageApp', $message));

  -        return $response;

  +        return [];

       }

   }

  

  Mutations: 1 untested, 0 tested

  Score:     0.00%

  Duration:  0.30s

   FAIL  Mutation score below expected: 0.0 %. Minimum: 100.0 %.

Algo va mal. ¿Pero qué? Ajá. He aquí el asombroso poder de las pruebas de mutación, explicado en los siguientes pasos:

  • PEST modifica el RcsService() en devolver siempre una matriz vacía

  • PEST ejecuta el archivo de prueba como un mutante

  • Queremos que el mutante falle, y así sea asesinado

  • No lo hace, pasa

  • Esto significa que su prueba es demasiado débil

Entonces, ¿por qué es tan débil? La conclusión es: deberías probar la carga útil devuelta por Vonage. Aparte del hecho de que esta API de Vonage nunca devuelve una matriz en blanco, deberías probar la carga útil devuelta. Usando ->toBeArray() en este caso no es suficiente.

Cómo solucionar sus puntos débiles

La prueba en realidad define la salida falsa que se da, por lo que es seguro asumir que lo que estamos probando aquí es que la carga útil devuelta (que le hemos proporcionado) no se cambia de ninguna manera. Como hemos definido $responsedeberíamos cambiar la prueba para asegurarnos de que coincide exactamente con esa estructura de datos.

expect($result)->toBe($response);

Ya está. Una línea, sólo cambiando esa afirmación. Ejecutando de nuevo las mutaciones PEST, obtenemos lo siguiente:

Tests:    1 passed (2 assertions)

  Duration: 0.26s

  Mutating application files...

  1 Mutations for 1 Files created

   RUN  app/Services/RcsService.php

  ✓ Line 20: AlwaysReturnEmptyArray

  Mutations: 1 tested

  Score:     100.00%

  Duration:  0.34s

Vaya.

Conclusión

Sé que, para empezar, se trata de un ejemplo bastante básico. Sin embargo, el concepto de lo que las pruebas de mutación proporciona es principalmente lo que estamos cubriendo aquí. A pesar de ser sólo unas pocas líneas de código, demuestra el gran poder de lo que las pruebas de mutación pueden hacer para endurecer su código base. Considere usarlo con algo como PHPStan, y los desarrolladores de PHP nunca lo han tenido mejor para enviar código reforzado.

¿Tienes alguna pregunta o algo que compartir? Únete a la conversación en Slack de la comunidad de Vonagey mantente actualizado con el Boletín para desarrolladoressíguenos en X (antes Twitter)suscríbete a nuestro canal de YouTube para ver tutoriales en video, y sigue la página de página para desarrolladores de Vonage en LinkedInun espacio para que los desarrolladores aprendan y se conecten con la comunidad. Mantente conectado, comparte tu progreso y entérate de las últimas noticias, consejos y eventos para desarrolladores.

Compartir:

https://a.storyblok.com/f/270183/400x385/12b3020c69/james-seconde.png
James SecondePromotor senior de desarrollo PHP

Actor de formación con una disertación sobre la comedia, llegué al desarrollo de PHP a través de la escena de las reuniones. Puedes encontrarme hablando y escribiendo sobre tecnología, o tocando/comprando discos raros de mi colección de vinilos.