Laravel Testing con Pest 5 nel 2026: TIA, Mocking e Domande da Colloquio

Best practice per il testing Laravel con Pest 5, Test Impact Analysis, Mockery, facade fake e test architetturali. Unit test, feature test, strategie di mocking e domande frequenti nei colloqui per sviluppatori Laravel.

Laravel Testing con Pest 4 e Mocking Best Practices 2026

Le best practice per il testing Laravel sono cambiate radicalmente con Pest 5, la TIA engine (Test Impact Analysis) e PHPUnit 13. Laravel 13 include il supporto nativo per Pest, rendendo l'esperienza di testing piu espressiva e meno verbosa rispetto a PHPUnit puro. Questa guida copre i pattern essenziali sia per le applicazioni in produzione che per i colloqui tecnici.

Pest 5 e lo Standard

Pest 5 (basato su PHPUnit 13) e il framework di testing predefinito per Laravel 13. Richiede PHP 8.4+ e introduce la TIA engine, verifica first-party per AI agent, integrazione PHPStan e sharding bilanciato nel tempo.

Configurare Pest 5 in un Progetto Laravel 13

Ogni nuova applicazione Laravel 13 viene scaffoldata automaticamente con Pest. Per i progetti esistenti, la migrazione da Pest 4 richiede pochi minuti, dato che la compatibilita con PHPUnit 13 rappresenta il cambiamento principale. La configurazione risiede in tests/Pest.php, dove vengono registrati i trait globali e i metodi helper.

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

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

Questo singolo file sostituisce il vecchio trait CreatesApplication e l'ereditarieta della classe base di test. Il trait RefreshDatabase incapsula ogni test in una transazione del database, annullando automaticamente le modifiche.

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();
});

L'API expect() di Pest si integra con le assertion di PHPUnit. La chiamata assertJsonStructure valida la struttura della risposta, mentre expect()->toBeTrue() conferma lo stato del database. Entrambi gli stili coesistono senza conflitti.

Test Impact Analysis: La Novita Principale di Pest 5

La TIA engine rende Pest 5 trasformativo per codebase di grandi dimensioni. Al primo run, Pest registra quali test toccano quali file. Ogni run successivo esegue solo i test influenzati dalle modifiche e riproduce i risultati in cache per tutto il resto.

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

# Subsequent runs: only affected tests execute
php artisan test

La test suite di Laravel Cloud, con oltre 19.000 test, e scesa da circa tre minuti a cinque secondi con TIA attivato. L'engine rileva piu dei soli file PHP: individua modifiche nelle migration, nei template Blade e nei componenti JavaScript condivisi.

TIA richiede un coverage driver (PCOV o Xdebug) per registrare la baseline. Per le pipeline CI, le mappature in cache persistono tra i run, rendendo il guadagno di performance cumulativo.

Unit Test vs Feature Test in Laravel

La distinzione tra unit test e feature test in Laravel determina quali parti del framework vengono avviate. Gli unit test vengono eseguiti senza il container dell'applicazione, risultando piu veloci ma limitati alla logica pura. I feature test avviano l'applicazione completa, abilitando chiamate HTTP, query al database e risoluzione dei servizi.

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);
    });
});

Gli unit test puntano a classi isolate senza dipendenze esterne. Il blocco describe raggruppa le assertion correlate, e Pest 5 supporta blocchi describe annidati per gerarchie di test complesse.

I feature test dovrebbero costituire la maggior parte di una test suite Laravel. Rilevano bug di integrazione che gli unit test non individuano, come middleware di route errati, regole di validazione mancanti o relazioni Eloquent difettose. La regola generale e: se il codice interagisce con il database, il livello HTTP o una facade, occorre scrivere un feature test.

Strategie di Mocking con le Facade e Mockery

Il mocking nel testing Laravel isola il codice sotto test dalle dipendenze esterne. Le facade di Laravel forniscono implementazioni fake integrate per code, eventi, notifiche, mail e storage. Mockery gestisce tutto il resto.

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);
    });
});

I facade fake intercettano le chiamate a livello di framework, impedendo l'invio effettivo di email o l'accodamento di job. Le assertion basate su closure verificano i dati esatti passati a ciascun componente.

Mock ai Confini

Le classi di dominio interne non dovrebbero essere mockate. Il mocking va applicato ai confini del sistema: API di terze parti, mail, code, filesystem. Un mocking eccessivo rende i test fragili e strettamente accoppiati ai dettagli implementativi.

Per le dipendenze che non sono facade, l'iniezione avviene tramite costruttore con 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');
});

Il binding del mock con $this->app->instance() sostituisce l'implementazione reale per la durata del test. Mockery verifica che charge sia stato chiamato esattamente una volta con gli argomenti attesi.

Test Architetturali con i Preset di Pest

Pest 5 include test architetturali che applicano regole strutturali all'intero codebase. Questi test vengono eseguiti sull'AST, non sul runtime, risultando estremamente veloci.

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 prima regola impedisce ai controller di accedere direttamente al database, imponendo un pattern a livello di servizio. La seconda garantisce che i servizi non possano essere estesi, riducendo la complessita dell'ereditarieta. La terza intercetta istruzioni di debug dimenticate prima che raggiungano la produzione.

Sono disponibili anche preset specifici per Laravel:

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

Queste tre righe applicano decine di regole: i model devono estendere la classe base corretta, i controller non devono contenere logica di business, funzioni insicure come eval() o md5() per l'hashing sono vietate, e le convenzioni PHP standard vengono rispettate.

Pronto a superare i tuoi colloqui su Laravel?

Pratica con i nostri simulatori interattivi, flashcards e test tecnici.

Mutation Testing per Verificare la Qualita dei Test

La code coverage misura quali righe vengono eseguite durante i test. Il mutation testing va oltre: modifica il codice sorgente e verifica se i test rilevano il cambiamento. Se una mutazione sopravvive, la test suite presenta una lacuna.

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

Pest 5 introduce mutazioni come il cambio di > in >=, la rimozione di istruzioni return e l'inversione di condizioni booleane. Un mutation score inferiore all'80% indica generalmente che i test verificano gli output senza controllare i casi 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);
});

Il primo test passa anche se la logica di calcolo e completamente errata, purche restituisca un float. Il secondo test fallisce immediatamente quando una mutazione modifica la formula. Assertion specifiche producono mutation score piu elevati.

Pattern di Testing del Database e Factory

Le model factory di Laravel generano dati di test realistici senza costruzione manuale di array. Pest 5 combinato con le factory produce un setup di dati leggibile e manutenibile.

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();
});

Lo state draft() della factory imposta valori predefiniti per gli articoli non pubblicati. Le assertion expect() concatenate con ->and() si leggono come linguaggio naturale e falliscono con messaggi descrittivi.

Testing Parallelo e Sharding

Eseguendo php artisan test --parallel i test vengono distribuiti su piu processi. Laravel crea automaticamente database di test separati per ogni processo, prevenendo conflitti tra i dati. Lo sharding bilanciato nel tempo di Pest 5 distribuisce i test in base al tempo di esecuzione effettivo invece che al numero, assicurando che ogni shard CI termini contemporaneamente.

Domande Frequenti sul Testing Laravel nei Colloqui

I colloqui tecnici per posizioni Laravel valutano frequentemente la conoscenza del testing. Ecco le domande piu ricorrenti, insieme a cio che una risposta efficace dovrebbe coprire.

Qual e la differenza tra fake(), mock() e spy() nel testing Laravel?

Facade fake() sostituisce l'intera facade con un'implementazione in memoria (Mail::fake, Queue::fake). mock() tramite Mockery imposta aspettative prima dell'esecuzione e fallisce se non vengono soddisfatte. spy() registra le interazioni e consente assertion dopo l'esecuzione, senza impostare aspettative preventive. La scelta dipende dalla necessita di verificare il comportamento (mock), registrare le interazioni (spy) o prevenire effetti collaterali (fake).

In che modo RefreshDatabase differisce da DatabaseTransactions?

RefreshDatabase esegue le migrazioni una volta e incapsula ogni test in una transazione. DatabaseTransactions presume che il database abbia gia lo schema corretto e si limita a incapsulare i test in transazioni. RefreshDatabase e piu sicuro per le pipeline CI dove il database potrebbe non esistere ancora. DatabaseTransactions e piu veloce quando lo schema e garantito essere aggiornato.

Quando i feature test sono preferibili agli unit test?

I feature test dovrebbero coprire qualsiasi codice che interagisce con il livello HTTP, il database o i servizi Laravel. Gli unit test sono riservati a funzioni pure e value object senza dipendenze dal framework. In un'applicazione Laravel tipica, i feature test superano gli unit test in un rapporto di circa 3:1 o superiore. Questo riflette la realta per cui la maggior parte del codice Laravel e intrinsecamente integrato con il framework.

Come Test Impact Analysis migliora le prestazioni CI?

TIA registra al primo run quali test toccano quali file sorgente. Nei run successivi, Pest esegue solo i test influenzati dai file modificati e riproduce i risultati in cache per i test invariati. Suite di grandi dimensioni che richiedevano minuti possono completarsi in secondi. Il compromesso: TIA richiede un coverage driver (PCOV o Xdebug) per costruire la mappa delle dipendenze, e il primo run richiede piu tempo di un run normale.

Come il mutation testing migliora la qualita dei test oltre la code coverage?

La code coverage misura i percorsi di esecuzione. Un test puo raggiungere il 100% di coverage chiamando ogni metodo senza effettuare assertion significative. Il mutation testing modifica il codice sorgente (cambia operatori, rimuove return, inverte booleani) e verifica che almeno un test fallisca. Le mutazioni sopravvissute rivelano assertion troppo permissive o del tutto assenti. php artisan test --mutate produce un mutation score percentuale insieme alla coverage standard.

Per ulteriori domande sui colloqui Laravel, la banca dati SharpSkill copre autenticazione, pattern del service container, relazioni Eloquent e architettura delle code.

Test delle Risposte HTTP e Assertion JSON

Gli helper di testing HTTP di Laravel verificano codici di stato, header, strutture JSON e destinazioni di redirect. Combinati con Pest, formano test di integrazione concisi.

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']);
    });
});

Il blocco describe raggruppa i test relativi all'autenticazione. Ogni nome di test descrive il comportamento atteso, non l'implementazione. Il metodo assertJsonValidationErrors verifica che campi specifici contengano messaggi di errore di validazione.

Sources

Punti Chiave sul Testing Laravel con Pest 5

  • Pest 5 con Laravel 13 elimina il boilerplate attraverso un'API di configurazione fluida, catene expect() e individuazione automatica dei test
  • La TIA engine riesegue solo i test interessati, riducendo l'esecuzione di suite di grandi dimensioni da minuti a secondi senza sacrificare l'accuratezza della coverage
  • I feature test dovrebbero costituire il nucleo di una test suite Laravel, con gli unit test riservati alla logica di business isolata
  • I facade fake (Mail::fake(), Queue::fake()) gestiscono il mocking a livello di framework, mentre Mockery gestisce le dipendenze di terze parti iniettate tramite il container
  • I test architetturali applicano regole strutturali (niente Eloquent nei controller, niente funzioni di debug) senza overhead a runtime
  • Il mutation testing con --mutate individua assertion deboli che la sola code coverage non rileva
  • I factory state e le assertion concatenate expect()->and() mantengono il setup dei dati di test leggibile e le assertion specifiche
  • Per la preparazione ai colloqui Laravel, la comprensione di mock/fake/spy, del comportamento di RefreshDatabase, TIA e mutation testing dimostra una maturita nel testing che va oltre la semplice coverage

Inizia a praticare!

Metti alla prova le tue conoscenze con i nostri simulatori di colloquio e test tecnici.

Sfida del giorno

Sapresti trovare il bug in Laravel?

Uno snippet reale, un bug nascosto, un tentativo al giorno. Senza account per provare.

Anthony Fillion-Maillet

Scritto da

Anthony Fillion-Maillet

Fondatore di SharpSkill

Sviluppatore fullstack da oltre 10 anni. Guida SharpSkill e risponde di tutto ciò che vi viene pubblicato.

Aggiornato il 2 settembre 2026

Tag

#laravel
#testing
#pest
#php
#interview

Condividi

Articoli correlati