Laravel Tdd

作者 affaan-mef648e01899b无许可证275K 个星标收录于 2026年10月8日更新于 2026年10月8日仓库3天前更新

Desarrollo guiado por pruebas para Laravel con PHPUnit y Pest, factories, pruebas de base de datos, fakes y objetivos de cobertura.

AI 生成的概览

指导 Laravel 的测试驱动开发,涵盖 PHPUnit 与 Pest、工厂、数据库测试、伪造与覆盖率目标。

功能
该技能为 Laravel 应用提供测试驱动开发流程,遵循红-绿-重构循环。它说明如何选择测试层次(单元、功能、集成)、数据库策略(如 RefreshDatabase),以及在 Pest 与 PHPUnit 之间选择测试框架。内容还涵盖工厂与状态、任务、队列、邮件、通知和事件的伪造、Sanctum 认证测试、HTTP 伪造、授权测试、Inertia 功能测试、覆盖率目标以及测试命令。
适用场景
适用于为 Laravel 应用新增功能或接口、修复缺陷或重构,以及为 Eloquent 模型、策略、任务和通知编写测试。也适合希望采用统一 TDD 方法并达到 80% 以上单元与功能覆盖率的团队。
运行要求
需要一个使用 PHPUnit 或 Pest 的 Laravel 项目、PHP,以及测试数据库配置(如内存中的 SQLite)。覆盖率测量需要 pcov 或 XDEBUG_MODE=coverage。该技能仅包含说明,不附带脚本。

Flujo de Trabajo TDD en Laravel

Desarrollo guiado por pruebas para aplicaciones Laravel usando PHPUnit y Pest con 80%+ de cobertura (unit + feature).

Cuándo Usar

  • Nuevas funcionalidades o endpoints en Laravel
  • Correcciones de bugs o refactorizaciones
  • Probar modelos Eloquent, policies, jobs y notifications
  • Preferir Pest para pruebas nuevas a menos que el proyecto ya esté estandarizado en PHPUnit

Cómo Funciona

Ciclo Rojo-Verde-Refactorizar

  1. Escribir una prueba fallida
  2. Implementar el cambio mínimo para que pase
  3. Refactorizar manteniendo las pruebas en verde

Capas de Prueba

  • Unit: clases PHP puras, objetos de valor, servicios
  • Feature: endpoints HTTP, autenticación, validación, policies
  • Integration: base de datos + colas + límites externos

Elegir capas según el alcance:

  • Usar pruebas Unit para lógica de negocio pura y servicios.
  • Usar pruebas Feature para HTTP, autenticación, validación y forma de respuesta.
  • Usar pruebas Integration cuando se validen BD/colas/servicios externos juntos.

Estrategia de Base de Datos

  • RefreshDatabase para la mayoría de pruebas feature/integration (ejecuta migraciones una vez por ejecución de prueba, luego envuelve cada prueba en una transacción cuando está soportado; las bases de datos en memoria pueden re-migrar por prueba)
  • DatabaseTransactions cuando el esquema ya está migrado y solo se necesita rollback por prueba
  • DatabaseMigrations cuando se necesita un migrate/fresh completo para cada prueba y se puede asumir el costo

Usar RefreshDatabase como predeterminado para pruebas que tocan la base de datos: para bases de datos con soporte de transacciones, ejecuta las migraciones una vez por ejecución de prueba (mediante un flag estático) y envuelve cada prueba en una transacción; para SQLite :memory: o conexiones sin transacciones, migra antes de cada prueba. Usar DatabaseTransactions cuando el esquema ya está migrado y solo se necesitan rollbacks por prueba.

Elección del Framework de Pruebas

  • Usar Pest por defecto para pruebas nuevas cuando esté disponible.
  • Usar PHPUnit solo si el proyecto ya lo estandariza o requiere herramientas específicas de PHPUnit.

Ejemplos

Ejemplo con PHPUnit

php
use App\Models\User;use Illuminate\Foundation\Testing\RefreshDatabase;use Tests\TestCase;
final class ProjectControllerTest extends TestCase{    use RefreshDatabase;
    public function test_owner_can_create_project(): void    {        $user = User::factory()->create();
        $response = $this->actingAs($user)->postJson('/api/projects', [            'name' => 'New Project',        ]);
        $response->assertCreated();        $this->assertDatabaseHas('projects', ['name' => 'New Project']);    }}

Ejemplo de Prueba Feature (Capa HTTP)

php
use App\Models\Project;use App\Models\User;use Illuminate\Foundation\Testing\RefreshDatabase;use Tests\TestCase;
final class ProjectIndexTest extends TestCase{    use RefreshDatabase;
    public function test_projects_index_returns_paginated_results(): void    {        $user = User::factory()->create();        Project::factory()->count(3)->for($user)->create();
        $response = $this->actingAs($user)->getJson('/api/projects');
        $response->assertOk();        $response->assertJsonStructure(['success', 'data', 'error', 'meta']);    }}

Ejemplo con Pest

php
use App\Models\User;use Illuminate\Foundation\Testing\RefreshDatabase;
use function Pest\Laravel\actingAs;use function Pest\Laravel\assertDatabaseHas;
uses(RefreshDatabase::class);
test('owner can create project', function () {    $user = User::factory()->create();
    $response = actingAs($user)->postJson('/api/projects', [        'name' => 'New Project',    ]);
    $response->assertCreated();    assertDatabaseHas('projects', ['name' => 'New Project']);});

Ejemplo de Prueba Feature con Pest (Capa HTTP)

php
use App\Models\Project;use App\Models\User;use Illuminate\Foundation\Testing\RefreshDatabase;
use function Pest\Laravel\actingAs;
uses(RefreshDatabase::class);
test('projects index returns paginated results', function () {    $user = User::factory()->create();    Project::factory()->count(3)->for($user)->create();
    $response = actingAs($user)->getJson('/api/projects');
    $response->assertOk();    $response->assertJsonStructure(['success', 'data', 'error', 'meta']);});

Factories y Estados

  • Usar factories para datos de prueba
  • Definir estados para casos límite (archivado, admin, trial)
php
$user = User::factory()->state(['role' => 'admin'])->create();

Pruebas de Base de Datos

  • Usar RefreshDatabase para estado limpio
  • Mantener las pruebas aisladas y deterministas
  • Preferir assertDatabaseHas sobre consultas manuales

Ejemplo de Prueba de Persistencia

php
use App\Models\Project;use Illuminate\Foundation\Testing\RefreshDatabase;use Tests\TestCase;
final class ProjectRepositoryTest extends TestCase{    use RefreshDatabase;
    public function test_project_can_be_retrieved_by_slug(): void    {        $project = Project::factory()->create(['slug' => 'alpha']);
        $found = Project::query()->where('slug', 'alpha')->firstOrFail();
        $this->assertSame($project->id, $found->id);    }}

Fakes para Efectos Secundarios

  • Bus::fake() para jobs
  • Queue::fake() para trabajo en cola
  • Mail::fake() y Notification::fake() para notificaciones
  • Event::fake() para eventos de dominio
php
use Illuminate\Support\Facades\Queue;
Queue::fake();
dispatch(new SendOrderConfirmation($order->id));
Queue::assertPushed(SendOrderConfirmation::class);
php
use Illuminate\Support\Facades\Notification;
Notification::fake();
$user->notify(new InvoiceReady($invoice));
Notification::assertSentTo($user, InvoiceReady::class);

Pruebas de Autenticación (Sanctum)

php
use Laravel\Sanctum\Sanctum;
Sanctum::actingAs($user);
$response = $this->getJson('/api/projects');$response->assertOk();

HTTP y Servicios Externos

  • Usar Http::fake() para aislar APIs externas
  • Verificar payloads salientes con Http::assertSent()

Objetivos de Cobertura

  • Aplicar 80%+ de cobertura para pruebas unit + feature
  • Usar pcov o XDEBUG_MODE=coverage en CI

Comandos de Prueba

  • php artisan test
  • vendor/bin/phpunit
  • vendor/bin/pest

Configuración de Pruebas

  • Usar phpunit.xml para establecer DB_CONNECTION=sqlite y DB_DATABASE=:memory: para pruebas rápidas
  • Mantener un entorno separado para pruebas para evitar tocar datos de desarrollo/producción

Pruebas de Autorización

php
use Illuminate\Support\Facades\Gate;
$this->assertTrue(Gate::forUser($user)->allows('update', $project));$this->assertFalse(Gate::forUser($otherUser)->allows('update', $project));

Pruebas Feature con Inertia

Al usar Inertia.js, verificar el nombre del componente y las props con los helpers de testing de Inertia.

php
use App\Models\User;use Inertia\Testing\AssertableInertia;use Illuminate\Foundation\Testing\RefreshDatabase;use Tests\TestCase;
final class DashboardInertiaTest extends TestCase{    use RefreshDatabase;
    public function test_dashboard_inertia_props(): void    {        $user = User::factory()->create();
        $response = $this->actingAs($user)->get('/dashboard');
        $response->assertOk();        $response->assertInertia(fn (AssertableInertia $page) => $page            ->component('Dashboard')            ->where('user.id', $user->id)            ->has('projects')        );    }}

Preferir assertInertia sobre aserciones JSON crudas para mantener las pruebas alineadas con las respuestas de Inertia.

来源与署名

来源:affaan-m/ecc位于docs/es/skills/laravel-tdd提交ef648e0

许可证: 无许可证

内容归原作者所有。SourceWeft 从公开仓库中收录这些内容。

举报或申请下架