Presentiamo Pest PHP, diventato l’impostazione predefinita a partire da Laravel 11, dal punto di vista di un ingegnere senior. Spiega, tenendo conto dell’introduzione in progetti reali, differenze rispetto a PHPUnit, costo di migrazione e uso dei test arch().
Da Laravel 11 in poi, quando crei un progetto con laravel new, Pest viene selezionato come framework di test predefinito. PHPUnit è stato per anni lo strumento standard per i test PHP, ma Pest si basa su PHPUnit offrendo una scrittura di test più concisa e leggibile.
Pest funziona sopra PHPUnit, quindi i test PHPUnit esistenti si eseguono così come sono. La migrazione può procedere gradualmente.
Dietro l’adozione ufficiale di Pest c’è l’obiettivo di ridurre l’attrito nello scrivere test. Eliminando la procedura rituale di definire una classe e scrivere un metodo, e fornendo una scrittura che permette di concentrarsi su “ciò che vogliamo testare”, diventa naturale acquisire l’abitudine di scrivere test.
La differenza più evidente è il modo in cui si scrivono i test.
Pest
PHPUnit
test('l\'utente può effettuare il login', function () { $user = User::factory()->create(); $response = $this->post('/login', [ 'email' => $user->email, 'password' => 'password', ]); $response->assertRedirect('/dashboard');});
class AuthTest extends TestCase{ public function test_user_can_login(): void { $user = User::factory()->create(); $response = $this->post('/login', [ 'email' => $user->email, 'password' => 'password', ]); $response->assertRedirect('/dashboard'); }}
La funzione test() di Pest riceve una closure. Non serve definire classi o metodi e l’intento del test è chiaro fin dalla prima riga. Si può usare anche it(). In inglese si legge come una frase naturale: it('can login', ...).
Il tratto distintivo di Pest sono le asserzioni in stile chain con expect().
test('l\'API elenco post ritorna JSON', function () { $posts = Post::factory(3)->create(); $response = $this->getJson('/api/posts'); expect($response->status())->toBe(200); expect($response->json('data'))->toHaveCount(3); expect($response->json('data.0.title'))->not->toBeEmpty();});
expect($value)->toBe(), ->toBeNull(), ->toContain(), ->toHaveCount() e simili permettono di scrivere asserzioni che si leggono in modo naturale. Puoi anche concatenare più asserzioni.
Test arch() — verifica automatica dell’architettura
arch() di Pest è un test che verifica la struttura della codebase. Puoi verificare automaticamente regole architetturali come “il controller non dipende direttamente dal modello” o “il modello estende Eloquent”.
test('i modelli estendono Eloquent', function () { arch()->expect('App\Models') ->toExtend(Illuminate\Database\Eloquent\Model::class);});test('i controller sono final', function () { arch()->expect('App\Http\Controllers') ->toBeFinal();});test('le classi service non dipendono dai controller', function () { arch()->expect('App\Services') ->not->toUse('App\Http\Controllers');});
I test arch() vengono eseguiti effettuando analisi statica del codice sorgente. Non inviano richieste HTTP né accedono al database, quindi sono estremamente veloci.
Pest include anche preset di regole architetturali comunemente usate.
test('rispetta le regole di architettura di Laravel', function () { arch()->preset()->laravel();});
Il preset laravel() verifica insieme regole conformi alle convenzioni tipiche di Laravel come naming dei modelli, ereditarietà dei controller e struttura dei middleware.
Usare gli helper di test di Laravel senza modifiche
Pest eredita il TestCase di Laravel, quindi puoi usare così come sono tutti gli helper di test di Laravel come actingAs(), assertDatabaseHas() e gli helper di test HTTP.
use App\Models\Post;use App\Models\User;test('l\'utente può eliminare i propri post', function () { $user = User::factory()->create(); $post = Post::factory()->for($user)->create(); $response = $this ->actingAs($user) ->delete("/posts/{$post->id}"); $response->assertRedirect('/posts'); $this->assertModelMissing($post);});test('non si possono eliminare i post di altri utenti', function () { $user = User::factory()->create(); $otherUser = User::factory()->create(); $post = Post::factory()->for($otherUser)->create(); $this ->actingAs($user) ->delete("/posts/{$post->id}") ->assertForbidden(); $this->assertModelExists($post);});
Impostandolo una volta in tests/Pest.php, RefreshDatabase viene abilitato automaticamente per tutti i test Feature. Puoi anche sovrascriverlo nel singolo file di test.
Pest e PHPUnit possono convivere nello stesso progetto. Non è necessario riscrivere i test PHPUnit esistenti: basta iniziare a scrivere i nuovi test in stile Pest.
./vendor/bin/pest # Esegui tutti i test con Pest (inclusi quelli PHPUnit)./vendor/bin/pest --filter="utente" # Filtra per nome del testphp artisan test # Anche il comando Artisan usa Pest
Da Laravel 11 in poi, php artisan test usa automaticamente Pest se installato.
Aggiungi prima la configurazione di uses() in tests/Pest.php
Scrivi i nuovi test in stile Pest
Migra gradualmente i test PHPUnit esistenti verificando il funzionamento
Non serve riscrivere tutti i test in fretta. La sintassi di Pest ha un costo di apprendimento inferiore rispetto a PHPUnit, quindi anche l’adozione da parte del team è relativamente fluida.
Pest non è una sostituzione di PHPUnit, ma un layer superiore che lo avvolge. Ha un’integrazione profonda con Laravel, al punto da essere selezionato di default dallo starter kit. Il costo di introduzione nei progetti esistenti è basso e basta iniziare dai nuovi test per beneficiarne.Aumentando la quantità di test scritti, si scoprono prima i bug e cala la barriera psicologica verso il refactoring. Pest è lo strumento che riduce “l’attrito stesso dello scrivere test”.
Documentazione ufficiale di Pest
Per tutte le funzionalità di Pest come dataset, coverage ed esecuzione parallela, consulta la documentazione ufficiale.