Saltar al contenido principal

¿Qué son las pruebas?

Las pruebas son un mecanismo automatizado para verificar que el código funciona como se espera. Al escribirlas puedes comprobar rápidamente si los cambios rompen algo del comportamiento existente. En un equipo, tener pruebas da confianza para modificar y revisar el código. Laravel incluye soporte de pruebas de fábrica y admite tanto Pest como PHPUnit. Al instalar el proyecto ya vienen preparados el archivo de configuración phpunit.xml y el directorio tests/.
Pest se construye sobre PHPUnit y permite escribir pruebas con una sintaxis más concisa y legible. Si empiezas ahora, es recomendable elegir Pest.

Estructura del directorio tests/

tests/
├── Feature/      # Pruebas de funcionalidad
│   └── ExampleTest.php
├── Unit/         # Pruebas unitarias
│   └── ExampleTest.php
└── TestCase.php
  • Feature/ — Pruebas de funcionalidad. Sirven para pruebas de mayor tamaño que incluyen solicitudes HTTP. Cubren la integración entre varios objetos o endpoints de API. La mayoría de tus pruebas irán aquí.
  • Unit/ — Pruebas unitarias. Sirven para pruebas pequeñas de una clase o método. La aplicación Laravel no se arranca, así que no puedes usar la base de datos ni otras funcionalidades del framework.

Crear una prueba

Crea una nueva clase de prueba con el comando Artisan make:test.
# Crea una prueba Feature (por defecto)
php artisan make:test TodoTest

# Crea una prueba Unit
php artisan make:test TodoTest --unit
Se genera tests/Feature/TodoTest.php.

Ejecutar las pruebas

Ejecuta las pruebas con php artisan test.
php artisan test
También puedes ejecutar vendor/bin/pest o vendor/bin/phpunit directamente, pero php artisan test ofrece un formato de salida más legible. Para ejecutar una suite concreta usa opciones.
# Solo pruebas Feature
php artisan test --testsuite=Feature

# Para al primer fallo
php artisan test --stop-on-failure

Prueba básica

Ejemplo simple de aserciones.
<?php

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

test('la cadena contiene el texto esperado', function () {
    $message = 'Hello, Laravel!';

    expect($message)->toContain('Laravel');
});
<?php

namespace Tests\Unit;

use PHPUnit\Framework\TestCase;

class ExampleTest extends TestCase
{
    public function test_true_is_true(): void
    {
        $this->assertTrue(true);
    }

    public function test_string_contains_laravel(): void
    {
        $message = 'Hello, Laravel!';

        $this->assertStringContainsString('Laravel', $message);
    }
}

Aserciones habituales

PestPHPUnitDescripción
expect($x)->toBe($y)$this->assertSame($y, $x)Estrictamente iguales
expect($x)->toEqual($y)$this->assertEquals($y, $x)Iguales (sin importar el tipo)
expect($x)->toBeTrue()$this->assertTrue($x)Es true
expect($x)->toBeFalse()$this->assertFalse($x)Es false
expect($x)->toBeNull()$this->assertNull($x)Es null
expect($x)->toContain($y)$this->assertStringContainsString($y, $x)Contiene el string
expect($x)->toHaveCount($n)$this->assertCount($n, $x)Número de elementos coincide

Pruebas HTTP

Las pruebas HTTP de Laravel simulan solicitudes a rutas sin necesidad de arrancar un servidor real. Es una funcionalidad potente disponible en las pruebas de Feature/.

Comprobar que una página se muestra

Con get() envías una solicitud GET y verificas la respuesta.
<?php

test('se muestra la portada', function () {
    $response = $this->get('/');

    $response->assertStatus(200);
});
<?php

namespace Tests\Feature;

use Tests\TestCase;

class ExampleTest extends TestCase
{
    public function test_top_page_is_displayed(): void
    {
        $response = $this->get('/');

        $response->assertStatus(200);
    }
}

Ejemplo de pruebas HTTP de una app ToDo

Con un modelo Todo, probamos cada operación CRUD.
<?php

use App\Models\Todo;
use Illuminate\Foundation\Testing\RefreshDatabase;

uses(RefreshDatabase::class);

test('se muestra la lista de ToDos', function () {
    Todo::factory()->count(3)->create();

    $response = $this->get('/todos');

    $response->assertStatus(200);
    $response->assertSee('Lista');
});

test('se puede crear un ToDo', function () {
    $response = $this->post('/todos', [
        'title' => 'Aprender Laravel',
    ]);

    $response->assertRedirect('/todos');
    $this->assertDatabaseHas('todos', ['title' => 'Aprender Laravel']);
});

test('se puede eliminar un ToDo', function () {
    $todo = Todo::factory()->create();

    $response = $this->delete("/todos/{$todo->id}");

    $response->assertRedirect('/todos');
    $this->assertDatabaseMissing('todos', ['id' => $todo->id]);
});
<?php

namespace Tests\Feature;

use App\Models\Todo;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;

class TodoTest extends TestCase
{
    use RefreshDatabase;

    public function test_todo_list_is_displayed(): void
    {
        Todo::factory()->count(3)->create();

        $response = $this->get('/todos');

        $response->assertStatus(200);
        $response->assertSee('Lista');
    }

    public function test_todo_can_be_created(): void
    {
        $response = $this->post('/todos', [
            'title' => 'Aprender Laravel',
        ]);

        $response->assertRedirect('/todos');
        $this->assertDatabaseHas('todos', ['title' => 'Aprender Laravel']);
    }

    public function test_todo_can_be_deleted(): void
    {
        $todo = Todo::factory()->create();

        $response = $this->delete("/todos/{$todo->id}");

        $response->assertRedirect('/todos');
        $this->assertDatabaseMissing('todos', ['id' => $todo->id]);
    }
}

Aserciones habituales de respuesta

MétodoDescripción
assertStatus(200)Verifica el código de estado HTTP
assertOk()Verifica que el código es 200
assertRedirect('/path')Verifica la redirección a la URL indicada
assertSee('texto')Verifica que el cuerpo contiene el texto
assertDontSee('texto')Verifica que el cuerpo no contiene el texto
assertJson([...])Verifica datos en una respuesta JSON
assertDatabaseHas('table', [...])Verifica que un registro existe en la BD
assertDatabaseMissing('table', [...])Verifica que un registro no existe en la BD
El trait RefreshDatabase reinicia la base de datos tras cada prueba. Sirve para evitar interferencias entre pruebas.

Entorno de pruebas

Configuración de phpunit.xml

Configura el entorno de pruebas en phpunit.xml en la raíz. Por defecto se usan drivers array para sesión y caché, evitando que queden datos entre pruebas.
<env name="APP_ENV" value="testing"/>
<env name="CACHE_STORE" value="array"/>
<env name="SESSION_DRIVER" value="array"/>
Para la base de datos es habitual usar SQLite en memoria, que no deja archivos.
<env name="DB_CONNECTION" value="sqlite"/>
<env name="DB_DATABASE" value=":memory:"/>

Archivo .env.testing

Si creas .env.testing en la raíz, se lee en lugar de .env al ejecutar pruebas. Es útil para separar la configuración de conexión o de servicios externos para pruebas.
APP_ENV=testing
DB_CONNECTION=sqlite
DB_DATABASE=:memory:
Si tienes caché de configuración, ejecuta php artisan config:clear antes de las pruebas. De lo contrario podría usarse una caché antigua.

Ejecución en paralelo

Cuando hay muchas pruebas, el tiempo se alarga. Con --parallel puedes ejecutar en varios procesos a la vez y reducir el tiempo. Primero instala el paquete brianium/paratest.
composer require brianium/paratest --dev
Después ejecuta con la opción --parallel.
php artisan test --parallel
Por defecto usa tantos procesos como núcleos de CPU. Indica el número con --processes.
php artisan test --parallel --processes=4
Con pruebas en paralelo, cada proceso debe usar una base de datos independiente. RefreshDatabase combinado con SQLite en memoria de phpunit.xml funciona sin problemas.

Próximos pasos

Pruebas HTTP

Aprende con más detalle a simular solicitudes, hacer pruebas con autenticación y aserciones JSON.
Última modificación el 13 de julio de 2026