Ir al contenido principal
Laravel, shipping fast.

Antes de escribir una aserción, responde a una pregunta: ¿de qué depende el consumidor de este endpoint? No de lo que el controlador hace por dentro. No de qué método se llamó. El consumidor ve tres cosas: el código de estado, la forma del cuerpo JSON y el efecto secundario que se le prometió. Prueba eso. Ninguna otra cosa sobrevive a una refactorización.

// tests/Feature/LicenseTest.php
it('lists licenses', function () {
    actingAsConsumer('licenses:read');

    fakeProvider([[
        'key' => 'lic_1',
        'name' => 'Acme',
        'domains' => ['acme.test'],
        'created_at' => '2026-01-29T12:00:00Z',
    ]]);

    $this->getJson('/api/licenses')
        ->assertOk()
        ->assertJsonPath('data.0.key', 'lic_1')
        ->assertJsonStructure(['data' => [
            ['key', 'name', 'domains', 'created_at'],
        ]]);
});

Este test no comprueba qué método privado se ejecutó ni con qué se llamó al driver. Llega a la ruta como lo haría un consumidor y comprueba la respuesta como lo haría un consumidor: primero el estado, después el contenido. Cambia mañana toda la integración que hay detrás de Licenses::all() y a este test le da igual, siempre que /api/licenses siga respondiendo con la forma documentada.

Compáralo con un test que hace su aserción sobre un colaborador:

// Mal: prueba el cableado, no la respuesta al cliente
it('creates a license', function () {
    actingAsConsumer('licenses:write');

    $this->mock(LicenseContract::class)
        ->shouldReceive('create')->once();

    $this->postJson('/api/licenses', [
        'name' => 'Acme',
        'domain' => 'acme.test',
    ]);
});

Esa versión se rompe en cuanto renombras un método o mueves un paso a un job, y pasa aunque el endpoint devuelva un 500. Un test debería romperse cuando cambia el contrato, que es exactamente cuando quieres enterarte, y en ningún otro momento.

Reemplazar el contrato sigue siendo útil, como hizo el Capítulo 6, cuando el proveedor no es de lo que trata el test. Úsalo para quitar al proveedor de en medio, no como la aserción.

Tests de feature antes que tests unitarios

Casi toda la suite de tests de una API debería estar formada por tests de feature.

Un test unitario demuestra que una función funciona aislada. Una API no entrega funciones aisladas. Entrega una petición que atraviesa la autenticación, el limitador, la validación, una facade, una llamada externa y un Resource. Prueba cualquiera de ellos por separado y no habrás demostrado nada sobre si la cadena funciona. Los bugs de una aplicación de Laravel viven en las costuras entre componentes, y un test de feature es el único tipo que las cruza.

Los tests unitarios que sí existen se ganan su lugar. License::fromProvider() en el Capítulo 3 encierra una decisión real y no hace falta una petición para llegar a ella, así que tiene dos tests unitarios. Ese es el listón: si un test necesita getJson() para demostrar algo, es un test de feature. Si bastan una llamada a un método y una aserción, es un test unitario.

No se pudo cargar el audio. Inténtalo de nuevo en un momento.