Vai al contenuto principale

Introduzione

Laravel offre un’API ricca per simulare richieste HTTP e verificarne le risposte. Puoi riprodurre nei test le richieste all’applicazione senza avviare un vero server HTTP.
<?php

test('l\'applicazione restituisce una risposta corretta', function () {
    $response = $this->get('/');

    $response->assertStatus(200);
});
<?php

namespace Tests\Feature;

use Tests\TestCase;

class ExampleTest extends TestCase
{
    public function test_the_application_returns_a_successful_response(): void
    {
        $response = $this->get('/');

        $response->assertStatus(200);
    }
}
Il metodo get() invia una richiesta GET all’applicazione e assertStatus() verifica il codice HTTP.
Durante l’esecuzione dei test il middleware CSRF è disabilitato automaticamente. Non è necessario disabilitarlo esplicitamente.

Effettuare richieste

Nei test puoi usare i metodi get, post, put, patch, delete. Non fanno vere richieste di rete, ma simulano internamente. Restituiscono un’istanza di Illuminate\Testing\TestResponse con molti metodi di asserzione.
<?php

test('richiesta di base', function () {
    $response = $this->get('/');

    $response->assertStatus(200);
});
<?php

namespace Tests\Feature;

use Tests\TestCase;

class ExampleTest extends TestCase
{
    public function test_a_basic_request(): void
    {
        $response = $this->get('/');

        $response->assertStatus(200);
    }
}
In un test consigliamo di inviare una sola richiesta. Più richieste nello stesso test possono generare comportamenti inattesi.

Header di richiesta

Con withHeaders() personalizzi gli header.
$response = $this->withHeaders([
    'X-Header' => 'Value',
])->post('/user', ['name' => 'Sally']);

$response->assertStatus(201);
Con withCookie() o withCookies().
$response = $this->withCookie('color', 'blue')->get('/');

$response = $this->withCookies([
    'color' => 'blue',
    'name'  => 'Taylor',
])->get('/');

Sessione e autenticazione

Con withSession() imposti dati di sessione.
$response = $this->withSession(['banned' => false])->get('/');
Con actingAs() esegui come utente autenticato.
use App\Models\User;

$user = User::factory()->create();

$response = $this->actingAs($user)
    ->withSession(['banned' => false])
    ->get('/');
Puoi indicare la guard nel secondo argomento.
$this->actingAs($user, 'web');
Per richieste da non autenticato:
$this->actingAsGuest();

Debug delle risposte

$response->dump();
$response->dumpHeaders();
$response->dumpSession();

$response->dd();
$response->ddHeaders();
$response->ddBody();
$response->ddJson();
$response->ddSession();

Test delle eccezioni

Con la facade Exceptions.
use App\Exceptions\InvalidOrderException;
use Illuminate\Support\Facades\Exceptions;

Exceptions::fake();

$response = $this->get('/order/1');

Exceptions::assertReported(InvalidOrderException::class);

Exceptions::assertReported(function (InvalidOrderException $e) {
    return $e->getMessage() === 'The order was invalid.';
});
Per verificare che non siano state emesse eccezioni:
Exceptions::assertNotReported(InvalidOrderException::class);
Exceptions::assertNothingReported();
Per disabilitare la gestione delle eccezioni:
$response = $this->withoutExceptionHandling()->get('/');
Per testare eccezioni lanciate da closure:
$this->assertThrows(
    fn () => (new ProcessOrder)->execute(),
    OrderInvalid::class
);

$this->assertThrows(
    fn () => (new ProcessOrder)->execute(),
    fn (OrderInvalid $e) => $e->orderId() === 123
);

$this->assertDoesntThrow(fn () => (new ProcessOrder)->execute());

Test di API JSON

Laravel offre helper per JSON: json, getJson, postJson, putJson, patchJson, deleteJson, optionsJson.
$response = $this->postJson('/api/user', ['name' => 'Sally']);

$response
    ->assertStatus(201)
    ->assertJson([
        'created' => true,
    ]);
Puoi accedere ai dati JSON come array:
expect($response['created'])->toBeTrue();
assertJson() verifica che i dati indicati siano contenuti nella risposta JSON. Il test passa anche se la risposta ha proprietà aggiuntive.

Corrispondenza esatta

$response
    ->assertStatus(201)
    ->assertExactJson([
        'created' => true,
    ]);

JSON path

$response
    ->assertStatus(201)
    ->assertJsonPath('team.owner.name', 'Darian');

// Con closure
$response->assertJsonPath('team.owner.name', fn (string $name) => strlen($name) >= 3);

Test JSON fluente

use Illuminate\Testing\Fluent\AssertableJson;

$response = $this->getJson('/users/1');

$response
    ->assertJson(fn (AssertableJson $json) =>
        $json->where('id', 1)
            ->where('name', 'Victoria Faith')
            ->where('email', fn (string $email) => str($email)->is('[email protected]'))
            ->whereNot('status', 'pending')
            ->missing('password')
            ->etc()
    );
etc() permette la presenza di proprietà non asserite. Senza etc(), se ci sono proprietà non asserite il test fallisce, aiutando a prevenire fughe di dati sensibili nella response.
Per verificare presenza/assenza:
$response->assertJson(fn (AssertableJson $json) =>
    $json->has('data')
        ->missing('message')
);

$response->assertJson(fn (AssertableJson $json) =>
    $json->hasAll(['status', 'data'])
        ->missingAll(['message', 'code'])
);

Collection JSON

$response
    ->assertJson(fn (AssertableJson $json) =>
        $json->has(3)
            ->first(fn (AssertableJson $json) =>
                $json->where('id', 1)
                    ->where('name', 'Victoria Faith')
                    ->missing('password')
                    ->etc()
            )
    );
Per applicare la stessa asserzione a ogni item usa each():
$response
    ->assertJson(fn (AssertableJson $json) =>
        $json->has(3)
            ->each(fn (AssertableJson $json) =>
                $json->whereType('id', 'integer')
                    ->whereType('name', 'string')
                    ->whereType('email', 'string')
                    ->missing('password')
                    ->etc()
            )
    );

Type assertion

$response->assertJson(fn (AssertableJson $json) =>
    $json->whereType('id', 'integer')
        ->whereAllType([
            'users.0.name' => 'string',
            'meta' => 'array'
        ])
);

// Più tipi separati da |
$response->assertJson(fn (AssertableJson $json) =>
    $json->whereType('name', 'string|null')
        ->whereType('id', ['string', 'integer'])
);
Tipi: string, integer, double, boolean, array, null.

Test di autenticazione

use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;

uses(RefreshDatabase::class);

test('l\'utente autenticato può vedere la dashboard', function () {
    $user = User::factory()->create();

    $response = $this->actingAs($user)->get('/dashboard');

    $response->assertStatus(200);
});

test('l\'utente non autenticato viene reindirizzato', function () {
    $response = $this->get('/dashboard');

    $response->assertRedirect('/login');
});
Con guard specifica:
$this->actingAs($user, 'api');

Esempio: registrazione utente

use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;

uses(RefreshDatabase::class);

test('l\'utente può registrarsi', function () {
    $response = $this->post('/register', [
        'name'                  => 'Test User',
        'email'                 => '[email protected]',
        'password'              => 'password',
        'password_confirmation' => 'password',
    ]);

    $response->assertRedirect('/dashboard');
    $this->assertDatabaseHas('users', ['email' => '[email protected]']);
});

test('email duplicata non registrabile', function () {
    User::factory()->create(['email' => '[email protected]']);

    $response = $this->post('/register', [
        'name'                  => 'Test User',
        'email'                 => '[email protected]',
        'password'              => 'password',
        'password_confirmation' => 'password',
    ]);

    $response->assertSessionHasErrors('email');
});

Test di sessione

$response = $this->withSession(['locale' => 'it'])->get('/');

$response->assertSessionHas('locale', 'it');

Elenco delle asserzioni di sessione

MetodoDescrizione
assertSessionHas($key, $value)Chiave e valore in sessione
assertSessionHasAll([...])Più chiavi in sessione
assertSessionHasErrors($keys)Errori di validazione presenti
assertSessionHasErrorsIn($bag, $keys)Errori in un error bag specifico
assertSessionHasNoErrors()Nessun errore
assertSessionDoesntHaveErrors()Nessun errore
assertSessionMissing($key)Chiave assente

Test di upload file

Con UploadedFile::fake() e Storage::fake().
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\Storage;

test('è possibile caricare l\'avatar', function () {
    Storage::fake('avatars');

    $file = UploadedFile::fake()->image('avatar.jpg');

    $response = $this->post('/avatar', [
        'avatar' => $file,
    ]);

    Storage::disk('avatars')->assertExists($file->hashName());
});
Per verificare l’assenza:
Storage::disk('avatars')->assertMissing('missing.jpg');

Personalizzare i file finti

UploadedFile::fake()->image('avatar.jpg', $width, $height)->size(100);

UploadedFile::fake()->create('document.pdf', $sizeInKilobytes);

UploadedFile::fake()->create(
    'document.pdf', $sizeInKilobytes, 'application/pdf'
);

Test di viste

Senza simulare la richiesta HTTP puoi renderizzare direttamente la vista.
$view = $this->view('welcome', ['name' => 'Taylor']);

$view->assertSee('Taylor');
TestView fornisce:
MetodoDescrizione
assertSee($value)La stringa è presente
assertSeeInOrder([...])Le stringhe sono in ordine
assertSeeText($value)Il testo è presente
assertSeeTextInOrder([...])I testi sono in ordine
assertDontSee($value)Non presente
assertDontSeeText($value)Testo non presente
Contenuto come stringa:
$contents = (string) $this->view('welcome');
Errori di validazione in una vista:
$view = $this->withViewErrors([
    'name' => ['Inserisci un nome valido.']
])->view('form');

$view->assertSee('Inserisci un nome valido.');

Test di componenti

$view = $this->blade(
    '<x-component :name="$name" />',
    ['name' => 'Taylor']
);

$view->assertSee('Taylor');

$view = $this->component(Profile::class, ['name' => 'Taylor']);

$view->assertSee('Taylor');

Elenco delle asserzioni sulle response

Metodi principali della classe Illuminate\Testing\TestResponse.

Status HTTP

MetodoDescrizione
assertOk()200
assertCreated()201
assertAccepted()202
assertNoContent($status = 204)204
assertBadRequest()400
assertUnauthorized()401
assertForbidden()403
assertNotFound()404
assertUnprocessable()422
assertTooManyRequests()429
assertInternalServerError()500
assertStatus($code)Status specifico
assertSuccessful()2xx
assertClientError()4xx
assertServerError()5xx

Redirect

MetodoDescrizione
assertRedirect($uri)Redirect all’URI
assertRedirectContains($string)URI contiene la stringa
assertRedirectToRoute($name, $params)Alla route con nome
assertRedirectToSignedRoute($name, $params)Alla signed route
assertRedirectBack()All’URL precedente

Contenuto

MetodoDescrizione
assertSee($value, $escaped = true)Presente nella response
assertSeeInOrder(array $values)In ordine
assertSeeText($value)Testo presente
assertDontSee($value)Non presente
assertDontSeeText($value)Testo non presente
assertContent($value)Body uguale
assertDownload($filename)È un download

JSON

MetodoDescrizione
assertJson(array $data)Contiene i dati indicati
assertExactJson(array $data)Corrispondenza esatta
assertJsonPath($path, $value)Valore al path
assertJsonMissingPath($path)Path assente
assertJsonStructure(array $structure)Struttura conforme
assertJsonCount($count, $key)Numero di elementi
assertJsonFragment(array $data)Frammento presente
assertJsonMissing(array $data)Dati assenti
assertJsonIsArray()È un array
assertJsonIsObject()È un oggetto
assertJsonValidationErrors($keys)Errori di validazione
assertJsonMissingValidationErrors($keys)Nessun errore per le chiavi
MetodoDescrizione
assertHeader($headerName, $value)Header presente
assertHeaderMissing($headerName)Header assente
assertCookie($name, $value)Cookie presente
assertCookieExpired($name)Cookie scaduto
assertCookieMissing($name)Cookie assente
assertPlainCookie($name, $value)Cookie non cifrato

Viste

MetodoDescrizione
assertViewIs($value)Vista restituita
assertViewHas($key, $value)Dati nella vista
assertViewHasAll(array $data)Più dati
assertViewMissing($key)Dato assente

Validazione

MetodoDescrizione
assertValid($keys)Nessun errore per le chiavi
assertInvalid($keys)Errori per le chiavi

Consigli per il TDD

I test HTTP si sposano bene con il TDD. Consigli:
Metti i test HTTP in tests/Feature/. Descrivere il comportamento dall’esterno (richiesta → risposta) chiarisce cosa implementare.
Nei test che usano il DB usa il trait RefreshDatabase. Il DB viene resettato dopo ogni test evitando interferenze.
Con User::factory()->create() crei rapidamente dati di test. Definisci gli stati (state) per test più leggibili.
Verifica una cosa per test per identificare più facilmente la causa dei fallimenti. Segui lo schema AAA (Arrange, Act, Assert).
Per le route protette scrivi sempre sia il caso “autenticato” sia “non autenticato”. Prevengono problemi di sicurezza.

Pagine correlate

Introduzione al testing

Le basi dei test in Laravel e l’uso di php artisan test.
Ultima modifica il 13 luglio 2026