Un test es código que verifica que tu código funciona correctamente. En lugar de probar manualmente en el navegador, escribes tests que se ejecutan solos y te avisan si algo se rompe.
| Tipo | ¿Qué prueba? | ¿Usa BD? | Velocidad |
|---|---|---|---|
| Unit | Una clase o función aislada | No | Muy rápido |
| Feature | Un flujo completo (HTTP → BD) | Sí | Más lento |
classUserTestextendsTestCase{
publicfunctiontest_nombre_descriptivo(): void{
// 1. Arrange — preparar los datos$user = User::factory()->create();
// 2. Act — ejecutar la acción$resultado = $user->nombreCompleto();
// 3. Assert — verificar el resultado$this->assertEquals('Juan Pérez', $resultado);
}
}
Los tests siguen el patrón AAA:
$this->assertEquals($esperado, $actual); // son iguales$this->assertTrue($valor); // es verdadero$this->assertFalse($valor); // es falso$this->assertNull($valor); // es null$this->assertCount(3, $coleccion); // tiene 3 elementos$this->assertInstanceOf(User::class, $obj); // es instancia de User// Para HTTP (Feature Tests)$response->assertStatus(200); // código HTTP 200$response->assertRedirect('/ruta'); // redirige$response->assertViewHas('users'); // la vista tiene variable$response->assertJson(['key' => 'value']); // respuesta JSON// Para base de datos$this->assertDatabaseHas('users', ['email' => 'juan@email.com']);
$this->assertDatabaseMissing('users', ['email' => 'juan@email.com']);
Prueban una clase de forma aislada. Las dependencias se simulan con mocks.
classUserServiceTestextendsTestCase{
publicfunctiontest_crea_usuario_correctamente(): void{
// Mock — simula el repositorio sin tocar la BD$repo = Mockery::mock(UserRepository::class);
$repo->shouldReceive('create')
->once()
->with(['name' => 'Juan'])
->andReturn(new User(['name' => 'Juan']));
$service = new UserService($repo);
$resultado = $service->crear(['name' => 'Juan']);
$this->assertEquals('Juan', $resultado->name);
}
}
Prueban el flujo completo: request HTTP → controlador → base de datos → respuesta.
classUserControllerTestextendsTestCase{
useRefreshDatabase; // resetea la BD entre cada testpublicfunctiontest_lista_usuarios(): void{
User::factory()->count(3)->create();
$response = $this->get('/users');
$response->assertStatus(200);
$response->assertViewHas('users');
}
publicfunctiontest_crea_usuario(): void{
$response = $this->post('/users', [
'name' => 'Juan',
'email' => 'juan@email.com',
]);
$response->assertRedirect('/users');
$this->assertDatabaseHas('users', ['email' => 'juan@email.com']);
}
publicfunctiontest_usuario_autenticado_puede_ver_perfil(): void{
$user = User::factory()->create();
$response = $this->actingAs($user)->get('/perfil');
$response->assertStatus(200);
}
}
Generan datos de prueba de forma rápida y limpia.
// Crear un usuario en BD$user = User::factory()->create();
// Crear sin guardar en BD$user = User::factory()->make();
// Crear varios$users = User::factory()->count(5)->create();
// Con datos específicos$admin = User::factory()->create(['role' => 'admin']);
// Con relaciones$user = User::factory()
->has(Order::factory()->count(3))
->create();
Patrón para centralizar la creación de objetos de prueba con nombres semánticos.
classUserMother{
publicstaticfunctionadmin(): User{
return User::factory()->create(['role' => 'admin']);
}
publicstaticfunctioninactivo(): User{
return User::factory()->create(['active' => false]);
}
publicstaticfunctionconPedidos(int $cantidad = 3): User{
return User::factory()
->has(Order::factory()->count($cantidad))
->create();
}
}
// En tus tests:$admin = UserMother::admin();
$inactivo = UserMother::inactivo();
Ventaja: los tests son más legibles y no repites configuración en cada archivo.
Simulan dependencias para aislar la unidad que estás probando.
// Mock básico$repo = Mockery::mock(UserRepository::class);
// Esperar que se llame un método$repo->shouldReceive('find')->once()->andReturn($user);
// Esperar que NO se llame$repo->shouldNotReceive('delete');
// Con parámetros específicos$repo->shouldReceive('find')->with(1)->andReturn($user);
# Todos los tests
php artisan test# Un archivo específico
php artisan test tests/Feature/UserControllerTest.php
# Un método específico
php artisan test --filter test_crea_usuario
# Con reporte de coverage en consola
php artisan test --coverage
# Con reporte de coverage en HTML
php artisan test --coverage-html reports/coverage
El reporte HTML se genera en la carpeta reports/coverage/ — ábrelo con el navegador para ver qué líneas están cubiertas y cuáles no.
test_usuario_inactivo_no_puede_iniciar_sesion es mejor que test_loginRefreshDatabase — para que cada test empiece con la BD limpiatests/
├── Unit/
│ ├── Services/
│ │ └── UserServiceTest.php
│ └── Models/
│ └── UserTest.php
├── Feature/
│ └── UserControllerTest.php
└── Mothers/
└── UserMother.php