Testing en Laravel con Pest 5 en 2026: TIA, Mocking y Preguntas de Entrevista

Domina las mejores practicas de testing en Laravel con Pest 5, Test Impact Analysis, Mockery, fakes de facades y tests de arquitectura. Cubre tests unitarios, tests de feature, estrategias de mocking y preguntas comunes de entrevista para desarrolladores Laravel.

Guia tecnica sobre testing en Laravel con Pest que cubre mocking, tests de arquitectura y preparacion para entrevistas tecnicas 2026

Las mejores practicas de testing en Laravel han cambiado significativamente con Pest 5, el motor TIA (Test Impact Analysis) y PHPUnit 13. Laravel 13 incluye soporte de primera clase para Pest, haciendo la experiencia de testing mas expresiva y menos verbosa que PHPUnit directo. Esta guia cubre los patrones que importan tanto para aplicaciones de produccion como para entrevistas tecnicas.

Pest 5 es el estandar

Pest 5 (construido sobre PHPUnit 13) es el framework de testing por defecto de Laravel 13. Requiere PHP 8.4+ e introduce el motor TIA, verificacion de agentes IA nativa, integracion con PHPStan y sharding balanceado por tiempo de ejecucion.

Configurar Pest 5 en un Proyecto Laravel 13

Cada nueva aplicacion Laravel 13 instala Pest automaticamente. Para proyectos existentes, la migracion desde Pest 4 toma minutos ya que la compatibilidad con PHPUnit 13 es la principal consideracion. La configuracion reside en tests/Pest.php, donde se registran los traits globales y los metodos helpers.

tests/Pest.phpphp
use Illuminate\Foundation\Testing\RefreshDatabase;

pest()
    ->extend(Tests\TestCase::class)
    ->use(RefreshDatabase::class)
    ->in('Feature');

Este unico archivo reemplaza el antiguo trait CreatesApplication y la herencia de clase base. El trait RefreshDatabase envuelve cada test en una transaccion de base de datos, revirtiendo los cambios automaticamente.

tests/Feature/UserRegistrationTest.phpphp
use App\Models\User;

it('registers a new user with valid data', function () {
    $response = $this->postJson('/api/register', [
        'name' => 'Jane Doe',
        'email' => 'jane@example.com',
        'password' => 'SecurePass123!',
        'password_confirmation' => 'SecurePass123!',
    ]);

    $response->assertStatus(201)
        ->assertJsonStructure(['user' => ['id', 'name', 'email']]);

    expect(User::where('email', 'jane@example.com')->exists())->toBeTrue();
});

La API expect() de Pest se encadena naturalmente con las aserciones de PHPUnit. La llamada assertJsonStructure valida la forma de la respuesta, mientras que expect()->toBeTrue() confirma el estado de la base de datos. Ambos estilos coexisten sin conflicto.

Test Impact Analysis: La Funcionalidad Estrella de Pest 5

El motor TIA es lo que hace de Pest 5 una herramienta transformadora para bases de codigo grandes. En la primera ejecucion, Pest registra que tests tocan que archivos. Cada ejecucion posterior ejecuta solo los tests afectados por los cambios y reproduce los resultados en cache para todo lo demas.

bash
# First run: records baseline (requires PCOV or Xdebug)
php artisan test

# Subsequent runs: only affected tests execute
php artisan test

La suite de tests de Laravel Cloud, conteniendo mas de 19,000 tests, bajo de aproximadamente tres minutos a cinco segundos con TIA habilitado. El motor entiende mas que archivos PHP: detecta cambios en migraciones, templates Blade y componentes JavaScript compartidos.

TIA requiere un driver de cobertura (PCOV o Xdebug) para registrar la linea base. Para pipelines de CI, los mappings en cache persisten entre ejecuciones, haciendo que la ganancia de rendimiento sea acumulativa.

Tests Unitarios vs Tests de Feature en Laravel

La distincion entre tests unitarios y tests de feature en Laravel determina que partes del framework se inician. Los tests unitarios se ejecutan sin el contenedor de la aplicacion, haciendolos mas rapidos pero limitados a logica pura. Los tests de feature inician la aplicacion completa, habilitando llamadas HTTP, consultas a base de datos y resolucion de servicios.

tests/Unit/PriceCalculatorTest.phpphp
use App\Services\PriceCalculator;

describe('PriceCalculator', function () {
    it('applies a percentage discount correctly', function () {
        $calculator = new PriceCalculator();

        // 20% off a 150.00 base price
        $result = $calculator->applyDiscount(150.00, 20);

        expect($result)->toBe(120.00);
    });

    it('rejects negative discount values', function () {
        $calculator = new PriceCalculator();

        expect(fn () => $calculator->applyDiscount(100.00, -5))
            ->toThrow(InvalidArgumentException::class);
    });
});

Los tests unitarios apuntan a clases aisladas sin dependencias externas. El bloque describe agrupa aserciones relacionadas, y Pest 5 soporta bloques describe anidados para jerarquias de tests complejas.

Los tests de feature deberian representar la mayoria de una suite de tests Laravel. Detectan bugs de integracion que los tests unitarios no captan, como middleware de ruta incorrecto, reglas de validacion faltantes o relaciones Eloquent defectuosas. Una regla comun: si el codigo toca la base de datos, la capa HTTP o una facade, escribe un test de feature.

Estrategias de Mocking con Facades y Mockery

El mocking en testing Laravel aisla el codigo bajo prueba de las dependencias externas. Las facades de Laravel proporcionan implementaciones fake integradas para colas, eventos, notificaciones, mail y storage. Mockery maneja todo lo demas.

tests/Feature/OrderProcessingTest.phpphp
use App\Models\Order;
use App\Models\User;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Facades\Queue;
use App\Mail\OrderConfirmation;
use App\Jobs\ProcessPayment;

it('dispatches payment job and sends confirmation email', function () {
    // Fake both Mail and Queue facades
    Mail::fake();
    Queue::fake();

    $user = User::factory()->create();
    $order = Order::factory()->for($user)->create([
        'total' => 99.99,
        'status' => 'pending',
    ]);

    // Act: confirm the order via HTTP
    $this->actingAs($user)
        ->postJson("/api/orders/{$order->id}/confirm")
        ->assertOk();

    // Assert: payment job was dispatched with correct amount
    Queue::assertPushed(ProcessPayment::class, function ($job) use ($order) {
        return $job->order->id === $order->id
            && $job->order->total === 99.99;
    });

    // Assert: confirmation email was sent to the user
    Mail::assertSent(OrderConfirmation::class, function ($mail) use ($user) {
        return $mail->hasTo($user->email);
    });
});

Los fakes de facades interceptan las llamadas a nivel del framework, evitando el envio real de emails o el despacho de jobs. Las aserciones basadas en closures verifican los datos exactos pasados a cada componente.

Mockear en los limites

Evita mockear clases de dominio internas. Mockea en los limites: APIs de terceros, mail, colas, sistemas de archivos. El exceso de mocking hace los tests fragiles y fuertemente acoplados a los detalles de implementacion.

Para dependencias que no son facades, inyectalas via constructor y usa Mockery:

tests/Feature/PaymentGatewayTest.phpphp
use App\Services\PaymentGateway;
use App\Services\StripeClient;

it('charges the customer through the payment gateway', function () {
    // Create a mock of the Stripe client
    $stripeClient = Mockery::mock(StripeClient::class);
    $stripeClient->shouldReceive('charge')
        ->once()
        ->with('cus_abc123', 5000, 'usd')
        ->andReturn(['status' => 'succeeded', 'id' => 'ch_xyz']);

    // Bind the mock in the container
    $this->app->instance(StripeClient::class, $stripeClient);

    $gateway = app(PaymentGateway::class);
    $result = $gateway->processCharge('cus_abc123', 50.00);

    expect($result['status'])->toBe('succeeded');
});

Vincular el mock con $this->app->instance() reemplaza la implementacion real por la duracion del test. Mockery verifica que charge fue llamado exactamente una vez con los argumentos esperados.

Tests de Arquitectura con Presets de Pest

Pest 5 incluye tests de arquitectura que imponen reglas estructurales en toda la base de codigo. Estos tests se ejecutan contra el AST, no en runtime, haciendolos extremadamente rapidos.

tests/Architecture/ArchitectureTest.phpphp
arch('controllers do not use Eloquent directly')
    ->expect('App\Http\Controllers')
    ->not->toUse('Illuminate\Database\Eloquent');

arch('services are final classes')
    ->expect('App\Services')
    ->toBeFinal();

arch('no debugging functions in production code')
    ->expect(['dd', 'dump', 'var_dump', 'ray'])
    ->not->toBeUsed();

La primera regla evita que los controladores consulten la base de datos directamente, imponiendo un patron de capa de servicio. La segunda asegura que los servicios no puedan extenderse, reduciendo la complejidad de herencia. La tercera detecta instrucciones de debug olvidadas antes de que lleguen a produccion.

Tambien hay presets especificos para Laravel disponibles:

tests/Architecture/LaravelPresetTest.phpphp
arch()->preset()->laravel();
arch()->preset()->security();
arch()->preset()->php();

Estas tres lineas imponen docenas de reglas: los modelos extienden la clase base correcta, los controladores no contienen logica de negocio, no hay funciones inseguras como eval() o md5() para hashing, y se siguen las convenciones estandar de PHP.

¿Listo para aprobar tus entrevistas de Laravel?

Practica con nuestros simuladores interactivos, flashcards y tests técnicos.

Mutation Testing para Verificar la Calidad de los Tests

La cobertura de codigo mide que lineas se ejecutan durante los tests. El mutation testing va mas alla: modifica el codigo fuente y verifica si los tests detectan el cambio. Si una mutacion sobrevive, la suite de tests tiene una brecha.

bash
# Run mutation testing on a specific class
php artisan test --mutate --class=App\\Services\\PriceCalculator

Pest 5 introduce mutaciones como cambiar > por >=, eliminar instrucciones return e invertir condiciones booleanas. Un score de mutacion por debajo del 80% generalmente indica que los tests verifican salidas sin revisar casos limite.

php
// Example: this test has high coverage but low mutation score
it('calculates shipping cost', function () {
    $cost = calculateShipping(weight: 5.0, zone: 'domestic');

    // Only checks that the result is numeric
    expect($cost)->toBeFloat();
});

// Improved: catches mutations by asserting the exact value
it('calculates domestic shipping for 5kg package', function () {
    $cost = calculateShipping(weight: 5.0, zone: 'domestic');

    // Exact assertion catches operator and value mutations
    expect($cost)->toBe(12.50);
});

El primer test pasa incluso si la logica de calculo esta completamente equivocada, siempre que retorne un float. El segundo test falla inmediatamente cuando una mutacion cambia la formula. Las aserciones especificas producen scores de mutacion mas altos.

Patrones de Testing de Base de Datos y Factories

Las model factories de Laravel generan datos de prueba realistas sin construccion manual de arrays. Pest 5 combinado con factories produce un setup de datos legible y mantenible.

tests/Feature/ArticlePublishingTest.phpphp
use App\Models\Article;
use App\Models\User;

it('publishes a draft article and updates the timestamp', function () {
    $author = User::factory()->create(['role' => 'editor']);

    $article = Article::factory()
        ->for($author, 'author')
        ->draft()
        ->create(['title' => 'Testing Best Practices']);

    $this->actingAs($author)
        ->patchJson("/api/articles/{$article->id}/publish")
        ->assertOk()
        ->assertJsonPath('data.status', 'published');

    $article->refresh();

    expect($article->status)->toBe('published')
        ->and($article->published_at)->not->toBeNull()
        ->and($article->published_at->isToday())->toBeTrue();
});

El estado de factory draft() establece valores por defecto para articulos no publicados. Las aserciones expect() encadenadas con ->and() se leen como lenguaje natural y fallan con mensajes descriptivos.

Tests Paralelos y Sharding

Ejecuta php artisan test --parallel para ejecutar tests en multiples procesos. Laravel crea automaticamente bases de datos de prueba separadas para cada proceso, evitando conflictos de datos. El sharding balanceado por tiempo de Pest 5 distribuye los tests por tiempo de ejecucion real en lugar de cantidad, asegurando que cada shard de CI termine al mismo momento.

Preguntas Comunes de Entrevista Tecnica sobre Testing en Laravel

Las entrevistas tecnicas para posiciones Laravel frecuentemente evaluan conocimientos de testing. Aqui estan las preguntas que aparecen mas a menudo, junto con lo que una buena respuesta debe cubrir.

Cual es la diferencia entre fake(), mock() y spy() en testing de Laravel?

El fake() de facade reemplaza toda la facade con una implementacion en memoria (Mail::fake, Queue::fake). mock() via Mockery establece expectativas antes de la ejecucion y falla si no se cumplen. spy() registra interacciones y permite aserciones despues de la ejecucion, sin establecer expectativas previas. La eleccion depende de si el test necesita verificar comportamiento (mock), registrar interacciones (spy) o prevenir efectos secundarios (fake).

En que difiere RefreshDatabase de DatabaseTransactions?

RefreshDatabase ejecuta las migraciones una vez y envuelve cada test en una transaccion. DatabaseTransactions asume que la base de datos ya tiene el esquema correcto y solo envuelve los tests en transacciones. RefreshDatabase es mas seguro para pipelines de CI donde la base de datos podria no existir. DatabaseTransactions es mas rapido cuando el esquema esta garantizado como actual.

Cuando se deben preferir los tests de feature sobre los tests unitarios?

Los tests de feature deberian cubrir cualquier codigo que interactue con la capa HTTP, base de datos o servicios de Laravel. Los tests unitarios se reservan para funciones puras y value objects sin dependencias del framework. En una aplicacion Laravel tipica, los tests de feature superan a los tests unitarios en una proporcion de aproximadamente 3:1 o mas. Esto refleja la realidad de que la mayoria del codigo Laravel esta inherentemente integrado con el framework.

Como mejora el Test Impact Analysis el rendimiento del CI?

TIA registra que tests tocan que archivos fuente durante la primera ejecucion. En ejecuciones posteriores, Pest ejecuta solo los tests afectados por archivos modificados y reproduce resultados en cache para tests sin cambios. Las suites grandes que tomaban minutos pueden completarse en segundos. El compromiso: TIA requiere un driver de cobertura (PCOV o Xdebug) para construir el mapa de dependencias, y la primera ejecucion toma mas tiempo que una ejecucion normal.

Como mejora el mutation testing la calidad de los tests mas alla de la cobertura de codigo?

La cobertura de codigo mide los caminos de ejecucion. Un test puede alcanzar 100% de cobertura llamando cada metodo sin afirmar nada significativo. El mutation testing modifica el codigo fuente (cambiando operadores, eliminando returns, invirtiendo booleanos) y verifica que al menos un test falle. Las mutaciones que sobreviven revelan aserciones demasiado amplias o faltantes. Ejecutar php artisan test --mutate produce un porcentaje de score de mutacion junto con la cobertura estandar.

Para mas preguntas de entrevista Laravel, el banco de preguntas de SharpSkill cubre autenticacion, patrones del contenedor de servicios, relaciones Eloquent y arquitectura de colas.

Testing de Respuestas HTTP y Aserciones JSON

Los helpers de testing HTTP de Laravel verifican codigos de estado, headers, estructuras JSON y destinos de redireccion. Combinados con Pest, forman tests de integracion concisos.

tests/Feature/ApiAuthenticationTest.phpphp
use App\Models\User;

describe('API Authentication', function () {
    it('rejects unauthenticated requests with 401', function () {
        $this->getJson('/api/profile')
            ->assertUnauthorized();
    });

    it('returns the authenticated user profile', function () {
        $user = User::factory()->create([
            'name' => 'John Doe',
            'email' => 'john@example.com',
        ]);

        $this->actingAs($user)
            ->getJson('/api/profile')
            ->assertOk()
            ->assertJson([
                'data' => [
                    'name' => 'John Doe',
                    'email' => 'john@example.com',
                ],
            ]);
    });

    it('validates required fields on registration', function () {
        $this->postJson('/api/register', [])
            ->assertUnprocessable()
            ->assertJsonValidationErrors(['name', 'email', 'password']);
    });
});

El bloque describe agrupa tests relacionados con autenticacion. Cada nombre de test describe el comportamiento esperado, no la implementacion. El metodo assertJsonValidationErrors verifica que campos especificos tienen mensajes de error de validacion.

Fuentes

Puntos Clave sobre Testing en Laravel con Pest 5

  • Pest 5 con Laravel 13 elimina el boilerplate mediante una API de configuracion fluida, cadenas expect() y descubrimiento automatico de tests
  • El motor TIA solo re-ejecuta tests afectados, reduciendo la ejecucion de suites grandes de minutos a segundos sin sacrificar precision de cobertura
  • Los tests de feature deberian formar el nucleo de una suite de tests Laravel, con tests unitarios reservados para logica de negocio aislada
  • Los fakes de facades (Mail::fake(), Queue::fake()) manejan el mocking a nivel de framework, mientras que Mockery maneja dependencias de terceros inyectadas via contenedor
  • Los tests de arquitectura imponen reglas estructurales (sin Eloquent en controladores, sin funciones de debug) sin costo en runtime
  • El mutation testing con --mutate detecta aserciones debiles que la cobertura de codigo sola no detecta
  • Los estados de factory y las aserciones encadenadas expect()->and() mantienen el setup de datos de prueba legible y las aserciones especificas
  • Para la preparacion de entrevistas Laravel, entender mock/fake/spy, el comportamiento de RefreshDatabase, TIA y mutation testing demuestra madurez en testing mas alla de la simple cobertura

¡Empieza a practicar!

Pon a prueba tu conocimiento con nuestros simuladores de entrevista y tests técnicos.

Reto diario

¿Sabrías detectar el bug en Laravel?

Un fragmento real, un bug oculto, un intento al día. Sin cuenta para probar.

Anthony Fillion-Maillet

Escrito por

Anthony Fillion-Maillet

Fundador de SharpSkill

Desarrollador fullstack desde hace más de 10 años. Dirige SharpSkill y responde por todo lo que se publica aquí.

Actualizado el 2 de septiembre de 2026

Etiquetas

#laravel
#testing
#pest
#php
#interview

Compartir

Artículos relacionados