Pest PHP: de cero a productivo en una tarde
Por qué cambié PHPUnit por Pest en todos mis proyectos y cómo montar una suite que de verdad se mantenga con el tiempo.
Durante años escribí tests con PHPUnit y me parecía bien. Después probé Pest en un proyecto pequeño y ya no he vuelto. No es que PHPUnit sea malo: es que Pest elimina el ruido que te separa de la intención del test.
La diferencia en una pantalla
Esto es PHPUnit:
class PostTest extends TestCase
{
public function test_a_guest_cannot_create_a_post(): void
{
$response = $this->post('/posts', ['title' => 'Hola']);
$response->assertRedirect('/login');
}
}Y esto es exactamente lo mismo en Pest:
it('does not let a guest create a post', function () {
$this->post('/posts', ['title' => 'Hola'])
->assertRedirect('/login');
});Se lee como una frase. Cuando la suite tiene trescientos tests, esa diferencia deja de ser estética y pasa a ser económica.
Datasets: el arma secreta
Aquí es donde Pest se despega de verdad. Los datasets convierten cinco tests copiados y pegados en uno solo:
it('rejects invalid emails', function (string $email) {
expect(fn () => User::create(['email' => $email]))
->toThrow(ValidationException::class);
})->with([
'sin arroba' => 'ismaelexample.com',
'sin dominio' => 'isma@',
'vacío' => '',
'con espacios' => 'isma @example.com',
]);Cada caso se reporta por separado con su nombre, así que cuando falla sabes exactamente cuál sin leer una sola línea de código.
Higher order tests
Para las comprobaciones triviales puedes encadenar directamente:
it('has a home page')
->get('/')
->assertOk();No abuses de esto. Funciona de maravilla para smoke tests y se vuelve ilegible en cuanto hay tres aserciones.
Cómo monto la suite
Mi tests/Pest.php casi siempre acaba así:
uses(Tests\TestCase::class, Illuminate\Foundation\Testing\RefreshDatabase::class)
->in('Feature');
expect()->extend('toBeSlug', function () {
return $this->toMatch('/^[a-z0-9]+(?:-[a-z0-9]+)*$/');
});Dos ideas detrás de esto:
RefreshDatabasesolo enFeature. Los tests unitarios no deberían tocar la base de datos; si uno la necesita, casi siempre es señal de que la lógica está en el sitio equivocado.- Expectations propias para el dominio.
toBeSlug,toBeValidInvoice,toBeWithinBusinessHours. El test acaba hablando el idioma del negocio en vez del idioma del framework.
Merece la pena
La migración desde PHPUnit es incremental: Pest ejecuta tus tests de PHPUnit sin tocarlos. Puedes instalarlo hoy, escribir los tests nuevos en Pest y no migrar nunca los viejos si no te apetece.
Esa es, probablemente, la mejor razón para probarlo.