Hai mai lanciato una suite di test Laravel e aspettato più di 10 secondi per un risultato? O peggio: hai visto un test fallire senza capire perché, e hai passato un'ora a inseguire un mock sbagliato? Succede a tutti. Il problema è che molti sviluppatori trattano i test come un obbligo noioso, non come uno strumento per scrivere codice migliore più velocemente.
Noi, di Meteora Web, lavoriamo con Laravel su progetti reali da anni: piattaforme proprietarie, e-commerce custom, API per clienti. E abbiamo sperimentato il cambiamento radicale che porta Pest PHP. Non è solo una sintassi più carina: è un cambio di paradigma nella leggibilità e nella velocità di scrittura dei test. In questa guida ti mostriamo come passare da test PHPUnit pesanti a test Pest snelli, e soprattutto come distinguere quando usare un unit test e quando un feature test per non sprecare tempo.
Perché Pest PHP è meglio del classico PHPUnit per i test Laravel?
PHPUnit è lo standard de facto, ma la sua sintassi con classi e metodi ereditati da TestCase rende i test verbosi. Ogni test richiede una classe, un metodo, e spesso un lungo setup. Pest riscrive l'esperienza: ti dà funzioni annidate, descrizioni in linguaggio naturale, e una chiarezza che fa la differenza quando torni sul codice dopo mesi.
Sponsored Protocol
Esempio concreto: lo stesso test in PHPUnit e Pest.
// PHPUnit class
class UserTest extends TestCase
{
public function test_it_creates_a_user()
{
$user = User::factory()->create();
$this->assertDatabaseHas('users', ['email' => $user->email]);
}
}
// Pest
test('creates a user', function () {
$user = User::factory()->create();
$this->assertDatabaseHas('users', ['email' => $user->email]);
});
Pest è più compatto, non richiede classi, e supporta le higher order tests per catenare asserzioni in modo fluido. Inoltre, le funzioni beforeEach() e afterEach() sostituiscono i metodi setUp() e tearDown() con una sintassi dichiarativa.
Come distinguere un Unit Test da un Feature Test in Laravel?
Questa è la domanda che fa più danni. Sbagliare livello di test significa o scrivere test fragili (che si rompono al primo refactoring) o test troppo lenti (che chiamano il database quando non serve).
Unit test: isolati e veloci
Un unit test verifica una singola classe o metodo, senza toccare il database, le route o altre dipendenze esterne. Usa mock o stub per isolare il soggetto. Quando usarlo: per servizi, helper, calcoli, logica di business pura. In Laravel, un esempio è testare un SubscriptionService che calcola la scadenza di un abbonamento.
Sponsored Protocol
// Pest - unit test con mock
test('subscription is active when end date is in future', function () {
$service = new SubscriptionService();
$user = Mockery::mock(User::class);
$user->shouldReceive('subscriptionEndAt')
->once()
->andReturn(now()->addDays(10));
expect($service->isActive($user))->toBeTrue();
});Velocità: questi test girano in millisecondi, nessuna connessione al database. Li lanci centinaia di volte durante lo sviluppo.
Feature test: integrazione end-to-end (ma leggera)
Un feature test in Laravel verifica un flusso completo: una richiesta HTTP, la logica del controller, l'interazione con il database, la risposta. Non usa mock per Eloquent o il database; usa la transazione del database (tramite RefreshDatabase o DatabaseTransactions). Devono essere più lenti, ma coprono la catena di valore.
// Pest - feature test per API
test('guests can register', function () {
$response = $this->postJson('/api/register', [
'name' => 'Mario Rossi',
'email' => 'mario@example.com',
'password' => 'Password123!',
'password_confirmation' => 'Password123!',
]);
$response->assertStatus(201)
->assertJsonStructure(['data' => ['token', 'user']]);
$this->assertDatabaseHas('users', ['email' => 'mario@example.com']);
});Regola pratica: se il test tocca il database o le route, è feature test. Se isola una singola classe, è unit test. Non mescolare mai i due nella stessa suite per non confondere i tempi di esecuzione.
Sponsored Protocol
Come strutturare i test con Pest in un progetto Laravel?
Pest si installa con composer require pestphp/pest --dev e si configura con un file tests/Pest.php. Noi di Meteora Web usiamo questa struttura:
tests/
├── Feature/
│ ├── Auth/
│ │ ├── LoginTest.php
│ │ └── RegisterTest.php
│ └── Api/
│ └── ProductsTest.php
├── Unit/
│ ├── Services/
│ │ └── SubscriptionServiceTest.php
│ └── Helpers/
│ └── PriceFormatterTest.php
├── Pest.php
└── TestCase.phpOgni file Test.php contiene più test con la sintassi test() o describe() che raggruppa i test correlati. Usa describe per organizzare i contesti:
describe('SubscriptionService', function () {
test('returns true for active subscriptions', function () { ... });
test('returns false for expired subscriptions', function () { ... });
});Pest genera automaticamente un'etichetta descrittiva nei report, rendendo i test molto più leggibili.
Sponsored Protocol
Quali best practice seguire per test veloci e affidabili?
Abbiamo visto troppi progetti con test che impiegano 3 minuti per girare. Ecco le nostre regole:
- Usa RefreshDatabase con criterio: per feature test, usa
RefreshDatabasesolo quando necessario; spessoDatabaseTransactionsè più veloce perché non esegue le migration a ogni test. - Non mockare ciò che non possiedi: Evita di mockare classi di librerie esterne a meno che non siano realmente lente. Usa i loro veri metodi quando possibile.
- Factories con stati: Definisci stati specifici nelle factory (es.
User::factory()->unverified()->create()) per creare dati precisi senza codice duplicato nei test. - Testa solo ciò che deve funzionare: Non coprire ogni setter con un test. Concentrati sui comportamenti critici: validazione, calcoli, autorizzazione, edge case.
- Usa il trait
WithFakercon moderazione: Preferisci dati deterministici per evitare test flaky.
Come eseguire i test in CI e integrarli con il flusso di lavoro?
Noi eseguiamo i test su ogni push con GitHub Actions. Pest si integra perfettamente con php artisan test (usa Pest di default se installato). Un esempio di workflow minimo:
Sponsored Protocol
name: Laravel Tests
on: [push]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: shivammathur/setup-php@v2
with:
php-version: '8.2'
- run: composer install --no-interaction
- run: cp .env.example .env
- run: php artisan key:generate
- run: php artisan migrate --force
- run: php artisan testCon Pest, i report di errore sono chiari e includono il contesto describe, facilitando il debugging.
Cosa fare adesso
- Installa Pest nel tuo progetto Laravel:
composer require pestphp/pest --deve segui la guida ufficiale per la migrazione. - Converti un bugfix esistente in test: prima di correggere un errore, scrivi un test che lo riproduce. Poi correggi. È il ciclo TDD più pragmatico.
- Separa unit test da feature test: sposta i test che non toccano il database in
tests/Unite quelli di integrazione intests/Feature. - Definisci tre stati per ogni factory: default, unverified, admin. Ti coprono l'80% dei casi.
- Leggi la documentazione ufficiale di Pest per approfondire le higher order tests e le architetture di test avanzate.
Se vuoi capire come inserire i test in un'architettura Laravel enterprise completa, visita la nostra pagina pillar su Laravel.