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.
Con withHeaders() personalizzi gli header.
$response = $this -> withHeaders ([
'X-Header' => 'Value' ,
]) -> post ( '/user' , [ 'name' => 'Sally' ]);
$response -> assertStatus ( 201 );
Cookie
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:
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
Metodo Descrizione 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:
Metodo Descrizione 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
Metodo Descrizione 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
Metodo Descrizione 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
Metodo Descrizione 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
Metodo Descrizione 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
Metodo Descrizione 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
Metodo Descrizione assertViewIs($value)Vista restituita assertViewHas($key, $value)Dati nella vista assertViewHasAll(array $data)Più dati assertViewMissing($key)Dato assente
Validazione
Metodo Descrizione 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).
Non dimenticare i test di autenticazione
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.