Saltar al contenido principal

¿Por qué Pest pasó a ser el valor por defecto en Laravel?

Desde Laravel 11, al crear un proyecto con laravel new, Pest se selecciona como framework de testing por defecto. PHPUnit se ha usado durante años como herramienta de testing estándar de PHP, pero Pest se apoya en PHPUnit y ofrece una notación de tests más concisa y legible.
Como Pest se ejecuta sobre PHPUnit, los tests PHPUnit existentes pueden ejecutarse tal cual. La migración se puede hacer de forma progresiva.
Detrás de la decisión oficial de adoptar Pest está el objetivo de reducir la fricción a la hora de escribir tests. Al eliminar los pasos ceremoniosos de definir una clase, escribir métodos, etc., y ofrecer una notación que permite concentrarse en “lo que quieres testear”, el hábito de escribir tests se adquiere de forma natural.

Diferencias principales con PHPUnit

Notación de los tests

La diferencia más evidente es la forma de escribir los tests.
test('el usuario puede iniciar sesión', function () {
    $user = User::factory()->create();

    $response = $this->post('/login', [
        'email' => $user->email,
        'password' => 'password',
    ]);

    $response->assertRedirect('/dashboard');
});
La función test() de Pest recibe un closure. Al no necesitar definir clase ni método, la intención del test queda clara desde la primera línea. it() se puede usar de la misma manera. Cuando se escribe en inglés, se lee como una frase natural: it('can login', ...).

API expect()

La mayor característica de Pest es la aserción en formato encadenado con expect().
test('la API de listado de posts devuelve JSON', function () {
    $posts = Post::factory(3)->create();

    $response = $this->getJson('/api/posts');

    expect($response->status())->toBe(200);
    expect($response->json('data'))->toHaveCount(3);
    expect($response->json('data.0.title'))->not->toBeEmpty();
});
Puedes escribir aserciones en un formato que se lee de forma natural, con expect($value)->toBe(), ->toBeNull(), ->toContain(), ->toHaveCount(), etc. También puedes agrupar varias aserciones con encadenamiento de métodos.
expect($user)
    ->name->toBe('Juan Pérez')
    ->email->toBe('[email protected]')
    ->is_admin->toBeFalse();

Setup y teardown

El equivalente a setUp() de PHPUnit es beforeEach(), y el de tearDown() es afterEach().
beforeEach(function () {
    $this->user = User::factory()->create();
    $this->actingAs($this->user);
});

test('los usuarios autenticados pueden acceder al perfil', function () {
    $response = $this->get('/profile');
    $response->assertOk();
});

test('los usuarios autenticados pueden actualizar el perfil', function () {
    $response = $this->patch('/profile', ['name' => 'Nombre nuevo']);
    $response->assertRedirect('/profile');
});

Datasets — tests table-driven

Para ejecutar la misma lógica de test con varios datos, se usa dataset.
test('las direcciones de correo inválidas dan error de validación', function (string $email) {
    $response = $this->postJson('/api/users', [
        'name' => 'Juan Pérez',
        'email' => $email,
    ]);

    $response->assertUnprocessable();
})->with([
    'sin email' => [''],
    'formato incorrecto' => ['notanemail'],
    'doble @' => ['a@@example.com'],
]);
Como la etiqueta de cada dataset ('sin email', etc.) se añade al nombre del test, sabes de un vistazo con qué patrón ha fallado.

Tests arch() — Comprobación automática de la arquitectura

arch() de Pest son tests que verifican la estructura del código base. Puedes verificar automáticamente reglas de arquitectura como “los controladores no dependen directamente de los modelos” o “los modelos heredan de Eloquent”.
test('los modelos heredan de Eloquent', function () {
    arch()->expect('App\Models')
        ->toExtend(Illuminate\Database\Eloquent\Model::class);
});

test('los controladores están definidos como final', function () {
    arch()->expect('App\Http\Controllers')
        ->toBeFinal();
});

test('las clases de servicio no dependen de los controladores', function () {
    arch()->expect('App\Services')
        ->not->toUse('App\Http\Controllers');
});
Los tests arch() se ejecutan mediante análisis estático del código fuente. Al no enviar peticiones HTTP reales ni acceder a la base de datos, son muy rápidos.
Pest también incluye presets con reglas de arquitectura de uso frecuente.
test('cumple con las reglas de arquitectura de Laravel', function () {
    arch()->preset()->laravel();
});
El preset laravel() verifica en bloque reglas alineadas con las convenciones habituales de Laravel: nombrado de modelos, herencia de controladores, estructura de middleware, etc.

Ejemplos prácticos en un proyecto Laravel

Crear tests

php artisan make:test UserTest
En Laravel 11 y posteriores, con la configuración por defecto, se genera un archivo de test en formato Pest.
<?php

test('example', function () {
    expect(true)->toBeTrue();
});

Usar los helpers de test de Laravel tal cual

Como Pest hereda del TestCase de Laravel, puedes usar tal cual todos los helpers de test de Laravel: actingAs(), assertDatabaseHas(), los helpers de tests HTTP, etc.
use App\Models\Post;
use App\Models\User;

test('el usuario puede borrar sus propios posts', function () {
    $user = User::factory()->create();
    $post = Post::factory()->for($user)->create();

    $response = $this
        ->actingAs($user)
        ->delete("/posts/{$post->id}");

    $response->assertRedirect('/posts');
    $this->assertModelMissing($post);
});

test('no se pueden borrar posts de otros usuarios', function () {
    $user = User::factory()->create();
    $otherUser = User::factory()->create();
    $post = Post::factory()->for($otherUser)->create();

    $this
        ->actingAs($user)
        ->delete("/posts/{$post->id}")
        ->assertForbidden();

    $this->assertModelExists($post);
});

Factories y transacciones de base de datos

También puedes aplicar fácilmente los traits RefreshDatabase o DatabaseTransactions con uses(). Si lo pones al principio del archivo, se aplica a todo el archivo.
uses(Tests\TestCase::class, Illuminate\Foundation\Testing\RefreshDatabase::class)->in('Feature');
Si lo configuras una vez en tests/Pest.php, RefreshDatabase se activa automáticamente en todos los tests Feature. También se puede sobrescribir en archivos de test individuales.

Cómo convivir con tests PHPUnit existentes

Pest y PHPUnit pueden convivir en el mismo proyecto. No es necesario reescribir los tests PHPUnit existentes; basta con empezar a escribir los tests nuevos en formato Pest.
./vendor/bin/pest       # Ejecuta todos los tests con Pest (incluidos los PHPUnit)
./vendor/bin/pest --filter="usuario"  # Filtra por nombre de test
php artisan test        # Con el comando Artisan también funciona Pest
Desde Laravel 11, si Pest está instalado, php artisan test ejecuta automáticamente con Pest.

Cómo abordar la migración

  1. Primero añade la configuración de uses() en tests/Pest.php
  2. Escribe los nuevos tests en formato Pest
  3. Ve migrando los tests PHPUnit existentes poco a poco, comprobando su funcionamiento
No hace falta reescribir todos los tests de golpe. Como la sintaxis de Pest tiene menor coste de aprendizaje que la de PHPUnit, la adopción por parte del equipo es relativamente fluida.

Conclusión

Elemento de comparaciónPHPUnitPest
Definición de testClase + métodoFunciones test() / it()
Aserción$this->assert*()expect()->to*()
SetupsetUp()beforeEach()
Data-driven@dataProvider->with()
Verificación de arquitecturaNoarch()
Velocidad de ejecuciónEstándarEquivalente (funciona sobre PHPUnit)
Pest no es un reemplazo de PHPUnit, sino una capa superior que envuelve a PHPUnit. La integración con Laravel también es profunda, hasta el punto de que se selecciona por defecto al generar los starter kits. El coste de introducción en proyectos existentes es bajo, y solo con probar a escribir los nuevos tests con Pest ya obtienes beneficios. Cuando aumenta la cantidad de tests escritos, se detectan antes los bugs y baja la barrera psicológica para refactorizar. Pest es una herramienta para reducir “la fricción de escribir tests” en sí.

Documentación oficial de Pest

Para todas las funciones de Pest (datasets, cobertura, ejecución en paralelo, etc.), consulta la documentación oficial.
Última modificación el 13 de julio de 2026