laravel-tdd
Desarrollo guiado por pruebas para Laravel con PHPUnit y Pest, factories, pruebas de base de datos, fakes y objetivos de cobertura.
What this skill does
# 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.
Related in General
modeling-omnistudio-epc-catalog
IncludedSalesforce Industries CME EPC product-modeling skill for Product2-based catalog creation. Use when creating EPC products, configuring product attributes, building offer bundles with Product Child Items, or reviewing EPC DataPack JSON metadata for product catalog changes. TRIGGER when: user creates or updates Product2 EPC records, AttributeAssignment payloads, AttributeMetadata/AttributeDefaultValues, Offer bundles, or ProductChildItem relationships. DO NOT TRIGGER when: designing OmniScripts/FlexCards/Integration Procedures (use building-omnistudio-omniscript, building-omnistudio-flexcard, or building-omnistudio-integration-procedure), implementing Apex business logic (use generating-apex), or troubleshooting deployment pipelines (use deploying-metadata).
relationship-science-coach
IncludedUse this skill for direct, practical adult relationship coaching: couples conflict, repair, trust, marriage, dating, flirting, attachment patterns, emotional connection, sex, desire differences, eroticism, kink negotiation, affection, love languages, breakups, and long-term passion. Draw on Gottman, EFT and Hold Me Tight, attachment science, modern sex research, Perel, Nagoski, Kerner, Schnarch, Love and Stosny, and flexible love-language tools. Be concrete and low-hedge. Redirect only for imminent danger, abuse, coercive control, minors, non-consent, self-harm, stalking, or medical/legal/psychiatric decisions.
building-sf-integrations
IncludedSalesforce integration architecture and runtime plumbing with 120-point scoring. Use this skill to set up Named Credentials, External Credentials, External Services, REST/SOAP callout patterns, Platform Events, and Change Data Capture. TRIGGER when: user sets up Named Credentials, External Services, REST/SOAP callouts, Platform Events, CDC, or touches .namedCredential-meta.xml files. DO NOT TRIGGER when: Connected App/OAuth config (use configuring-connected-apps), Apex-only logic (use generating-apex), or data import/export (use handling-sf-data).
venue-templates
IncludedAccess comprehensive LaTeX templates, formatting requirements, and submission guidelines for major scientific publication venues (Nature, Science, PLOS, IEEE, ACM), academic conferences (NeurIPS, ICML, CVPR, CHI), research posters, and grant proposals (NSF, NIH, DOE, DARPA). This skill should be used when preparing manuscripts for journal submission, conference papers, research posters, or grant proposals and need venue-specific formatting requirements and templates.
let-fate-decide
IncludedDraws the 12 Houses of the Zodiac Tarot spread to inject entropy into planning when prompts are vague, ambiguous, or casually delegated. Interprets the spread to guide next steps. Use when the user says 'let fate decide', 'YOLO', 'whatever', 'idk', or other nonchalant phrases, makes Yu-Gi-Oh references, or when you are about to arbitrarily pick between multiple reasonable approaches. Prefer over ask-questions-if-underspecified when the user's tone is casual or playful rather than precision-seeking.
net-ops
IncludedCross-platform network troubleshooting (Windows, macOS, Linux) via local or remote shell. Use for: DNS broken, can't resolve hostnames, nslookup/dig works but apps fail, NRPT, WFP, scutil, /etc/resolver, systemd-resolved, /etc/resolv.conf, NetworkManager, VPN DNS leak residue (ProtonVPN/Mullvad/WireGuard/AnyConnect), AV/firewall blocking DNS or DoH, Tailscale DNS interaction, intermittent connectivity, remote diagnostics over SSH.