Introduction
Laravel fournit une API riche pour simuler des requêtes HTTP et vérifier les réponses, sans démarrer de vrai serveur.
<? php
test ( 'l \' application retourne une réponse OK' , 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 );
}
}
get() envoie une requête GET simulée ; assertStatus() vérifie le code HTTP.
Le middleware CSRF est automatiquement désactivé pendant les tests.
Créer des requêtes
Utilisez get, post, put, patch, delete dans vos tests. Aucune vraie requête réseau ; le retour est une Illuminate\Testing\TestResponse.
<? php
test ( 'requête de 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 );
}
}
Une seule requête par test recommandé. Plusieurs peuvent produire des comportements inattendus.
Personnaliser les en-têtes
$response = $this -> withHeaders ([
'X-Header' => 'Value' ,
]) -> post ( '/user' , [ 'name' => 'Sally' ]);
Cookies
$response = $this -> withCookie ( 'color' , 'blue' ) -> get ( '/' );
$response = $this -> withCookies ([
'color' => 'blue' ,
'name' => 'Taylor' ,
]) -> get ( '/' );
Session et authentification
$response = $this -> withSession ([ 'banned' => false ]) -> get ( '/' );
actingAs() connecte un utilisateur.
use App\Models\ User ;
$user = User :: factory () -> create ();
$response = $this -> actingAs ( $user )
-> withSession ([ 'banned' => false ])
-> get ( '/' );
Précisez la garde en 2ᵉ argument.
$this -> actingAs ( $user , 'web' );
Déconnecter :
Débogage de la réponse
$response -> dump ();
$response -> dumpHeaders ();
$response -> dumpSession ();
Pour arrêter l’exécution :
$response -> dd ();
$response -> ddHeaders ();
$response -> ddBody ();
$response -> ddJson ();
$response -> ddSession ();
Tests d’exceptions
Utilisez la façade 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.' ;
});
assertNotReported / assertNothingReported pour l’inverse.
Exceptions :: assertNotReported ( InvalidOrderException :: class );
Exceptions :: assertNothingReported ();
Désactiver la gestion des exceptions :
$response = $this -> withoutExceptionHandling () -> get ( '/' );
assertThrows() :
$this -> assertThrows (
fn () => ( new ProcessOrder ) -> execute (),
OrderInvalid :: class
);
$this -> assertThrows (
fn () => ( new ProcessOrder ) -> execute (),
fn ( OrderInvalid $e ) => $e -> orderId () === 123
);
assertDoesntThrow() :
$this -> assertDoesntThrow ( fn () => ( new ProcessOrder ) -> execute ());
Tests JSON
json, getJson, postJson, putJson, patchJson, deleteJson, optionsJson.
$response = $this -> postJson ( '/api/user' , [ 'name' => 'Sally' ]);
$response
-> assertStatus ( 201 )
-> assertJson ([
'created' => true ,
]);
Accès aux données comme à un tableau :
expect ( $response [ 'created' ]) -> toBeTrue ();
// ou en PHPUnit :
$this -> assertTrue ( $response [ 'created' ]);
assertJson() vérifie qu’un fragment JSON est présent (autres propriétés autorisées).
Correspondance exacte
assertExactJson() exige une égalité stricte.
$response = $this -> postJson ( '/user' , [ 'name' => 'Sally' ]);
$response
-> assertStatus ( 201 )
-> assertExactJson ([
'created' => true ,
]);
JSON path
$response = $this -> postJson ( '/user' , [ 'name' => 'Sally' ]);
$response
-> assertStatus ( 201 )
-> assertJsonPath ( 'team.owner.name' , 'Darian' );
Avec closure :
$response -> assertJsonPath ( 'team.owner.name' , fn ( string $name ) => strlen ( $name ) >= 3 );
Fluent JSON
Utilisez AssertableJson.
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() autorise des propriétés supplémentaires. Sans etc(), une propriété inattendue fait échouer le test — cela évite des fuites accidentelles.
has() / missing() :
$response -> assertJson ( fn ( AssertableJson $json ) =>
$json -> has ( 'data' )
-> missing ( 'message' )
);
hasAll() / missingAll() :
$response -> assertJson ( fn ( AssertableJson $json ) =>
$json -> hasAll ([ 'status' , 'data' ])
-> missingAll ([ 'message' , 'code' ])
);
Collections JSON
$response
-> assertJson ( fn ( AssertableJson $json ) =>
$json -> has ( 3 )
-> first ( fn ( AssertableJson $json ) =>
$json -> where ( 'id' , 1 )
-> where ( 'name' , 'Victoria Faith' )
-> missing ( 'password' )
-> etc ()
)
);
each() pour vérifier tous les éléments :
$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 ()
)
);
Assertions de type
$response -> assertJson ( fn ( AssertableJson $json ) =>
$json -> whereType ( 'id' , 'integer' )
-> whereAllType ([
'users.0.name' => 'string' ,
'meta' => 'array'
])
);
Union de types via | :
$response -> assertJson ( fn ( AssertableJson $json ) =>
$json -> whereType ( 'name' , 'string|null' )
-> whereType ( 'id' , [ 'string' , 'integer' ])
);
Types : string, integer, double, boolean, array, null.
Tests d’authentification
use App\Models\ User ;
use Illuminate\Foundation\Testing\ RefreshDatabase ;
uses ( RefreshDatabase :: class );
test ( 'les utilisateurs authentifiés voient le dashboard' , function () {
$user = User :: factory () -> create ();
$response = $this -> actingAs ( $user ) -> get ( '/dashboard' );
$response -> assertStatus ( 200 );
});
test ( 'les non authentifiés sont redirigés' , function () {
$response = $this -> get ( '/dashboard' );
$response -> assertRedirect ( '/login' );
});
Garde précise : $this->actingAs($user, 'api');.
Exemple : inscription
use App\Models\ User ;
use Illuminate\Foundation\Testing\ RefreshDatabase ;
uses ( RefreshDatabase :: class );
test ( 'inscription réussie' , 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 dupliqué' , 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' );
});
Tests de session
$response = $this -> withSession ([ 'locale' => 'fr' ]) -> get ( '/' );
$response -> assertSessionHas ( 'locale' , 'fr' );
Assertions de session
Méthode Description assertSessionHas($key, $value)La clé existe avec la valeur assertSessionHasAll([...])Plusieurs clés/valeurs assertSessionHasErrors($keys)Erreurs de validation présentes assertSessionHasErrorsIn($bag, $keys)Erreurs dans une error bag précise assertSessionHasNoErrors()Pas d’erreurs assertSessionDoesntHaveErrors()Idem assertSessionMissing($key)Absente
Upload de fichiers
UploadedFile::fake() + Storage::fake().
use Illuminate\Http\ UploadedFile ;
use Illuminate\Support\Facades\ Storage ;
test ( 'upload d \' avatar' , function () {
Storage :: fake ( 'avatars' );
$file = UploadedFile :: fake () -> image ( 'avatar.jpg' );
$response = $this -> post ( '/avatar' , [
'avatar' => $file ,
]);
Storage :: disk ( 'avatars' ) -> assertExists ( $file -> hashName ());
});
Assertions Storage
Storage :: disk ( 'avatars' ) -> assertExists ( 'avatar.jpg' );
Storage :: disk ( 'avatars' ) -> assertMissing ( 'other.jpg' );
Storage :: disk ( 'avatars' ) -> assertCount ( 1 );
Storage :: disk ( 'avatars' ) -> assertEmpty ();
Fake images / fichiers
$file = UploadedFile :: fake () -> image ( 'avatar.jpg' );
$file = UploadedFile :: fake () -> image ( 'avatar.jpg' , 640 , 480 );
$file = UploadedFile :: fake () -> create ( 'document.pdf' , 1024 , 'application/pdf' );
Assertions de réponse
Méthode Description assertStatus($code)Code HTTP assertOk()200 assertCreated()201 assertNoContent()204 assertNotFound()404 assertUnauthorized()401 assertForbidden()403 assertUnprocessable()422 assertRedirect($uri)Redirection assertRedirectToRoute($name)Vers route nommée assertSee($value)Chaîne dans la réponse assertDontSee($value)Absente assertSeeText($value)Chaîne présente (texte échappé) assertViewIs($name)Vue rendue assertViewHas($key, $value)Donnée passée à la vue assertViewMissing($key)Absente assertJson([...])Fragment JSON assertExactJson([...])JSON exact assertJsonCount($count)Nombre d’éléments assertJsonFragment([...])Fragment dans un tableau assertJsonMissing([...])Fragment absent assertJsonStructure([...])Structure assertJsonValidationErrors($keys)Erreurs de validation JSON assertHeader($key, $value)En-tête assertHeaderMissing($key)En-tête absent assertCookie($name, $value)Cookie assertCookieExpired($name)Cookie expiré assertPlainCookie($name, $value)Cookie non chiffré assertSessionHas($key, $value)Session assertSessionHasErrors($keys)Erreurs en session assertDatabaseHas($table, [...])Enregistrement en base assertDatabaseMissing($table, [...])Absent en base
Assertions de validation
$response = $this -> post ( '/user' , []);
$response -> assertInvalid ([ 'email' => 'required' ]);
// Message précis
$response -> assertInvalid ([ 'email' => 'The email field is required.' ]);
// Une seule clé
$response -> assertInvalid ( 'email' );
// Plusieurs clés
$response -> assertInvalid ([ 'name' , 'email' ]);
assertValid() inverse.
$response -> assertValid ();
$response -> assertValid ( 'email' );
$response -> assertValid ([ 'name' , 'email' ]);
Middleware bypass
Ignorer temporairement des middlewares.
$response = $this -> withoutMiddleware () -> get ( '/dashboard' );
// Précis
$response = $this -> withoutMiddleware ([ Authenticate :: class ]) -> get ( '/dashboard' );
Utile pour se concentrer sur la logique du contrôleur, mais préférez tester avec les middlewares réels quand c’est possible.
Événements, jobs, mails, notifications
Event::fake()
use App\Events\ OrderShipped ;
use Illuminate\Support\Facades\ Event ;
Event :: fake ();
// Action déclenchant l'événement...
Event :: assertDispatched ( OrderShipped :: class );
Event :: assertDispatched ( OrderShipped :: class , function ( $event ) use ( $order ) {
return $event -> order -> id === $order -> id ;
});
Event :: assertNotDispatched ( OrderShipped :: class );
Event :: assertDispatchedTimes ( OrderShipped :: class , 3 );
Queue::fake()
use App\Jobs\ ShipOrder ;
use Illuminate\Support\Facades\ Queue ;
Queue :: fake ();
ShipOrder :: dispatch ( $order );
Queue :: assertPushed ( ShipOrder :: class );
Queue :: assertPushed ( ShipOrder :: class , function ( $job ) use ( $order ) {
return $job -> order -> id === $order -> id ;
});
Queue :: assertNotPushed ( ShipOrder :: class );
Queue :: assertPushedOn ( 'processing' , ShipOrder :: class );
Queue :: assertNothingPushed ();
Mail::fake()
use App\Mail\ OrderShipped ;
use Illuminate\Support\Facades\ Mail ;
Mail :: fake ();
// Envoi...
Mail :: assertSent ( OrderShipped :: class );
Mail :: assertSent ( OrderShipped :: class , function ( $mail ) use ( $order ) {
return $mail -> order -> id === $order -> id ;
});
Mail :: assertSent ( OrderShipped :: class , function ( $mail ) {
return $mail -> hasTo ( '[email protected] ' );
});
Mail :: assertNotSent ( OrderShipped :: class );
Mail :: assertNothingSent ();
Mail :: assertSentTimes ( OrderShipped :: class , 3 );
Notification::fake()
use App\Notifications\ OrderShipped ;
use Illuminate\Support\Facades\ Notification ;
Notification :: fake ();
// Envoi...
Notification :: assertSentTo ( $user , OrderShipped :: class );
Notification :: assertSentTo ( $users , OrderShipped :: class );
Notification :: assertNotSentTo ( $user , OrderShipped :: class );
Notification :: assertNothingSent ();
Assertions de vue
$response = $this -> get ( '/user/1' );
$response -> assertViewIs ( 'user.profile' );
$response -> assertViewHas ( 'user' );
$response -> assertViewHas ( 'user' , function ( $user ) {
return $user -> id === 1 ;
});
// Passer des données à la vue directement
$view = $this -> view ( 'welcome' , [ 'name' => 'Taylor' ]);
$view -> assertSee ( 'Taylor' );
blade() évalue une chaîne Blade brute :
$view = $this -> blade (
'<x-alert type="error">{{ $slot }}</x-alert>' ,
[ 'slot' => 'Error message' ]
);
$view -> assertSee ( 'Error message' );
Récapitulatif
Méthode Usage $this->get($uri)GET $this->post($uri, $data)POST $this->put($uri, $data)PUT $this->patch($uri, $data)PATCH $this->delete($uri)DELETE $this->getJson($uri)GET JSON $this->postJson($uri, $data)POST JSON $this->actingAs($user)Authentification $this->withHeaders([...])En-têtes $this->withSession([...])Session $this->withCookie('key', 'value')Cookies $this->withoutMiddleware()Sans middlewares $this->withoutExceptionHandling()Sans gestion d’exceptions
Assertion Vérifie assertStatus($code)Code HTTP assertOk() / assertCreated() / assertNotFound()Codes usuels assertRedirect($uri)Redirection assertSee($text)Texte présent assertJson([...])Fragment JSON assertExactJson([...])Correspondance exacte assertJsonPath('team.owner.name', 'Darian')Chemin JSON assertSessionHasErrors($keys)Erreurs assertDatabaseHas($table, [...])Enregistrement présent assertInvalid($keys)Validation KO assertValid()Validation OK
Fake Rôle Event::fake()Empêche l’exécution des événements Queue::fake()Empêche l’exécution des jobs Mail::fake()Empêche l’envoi de mail Notification::fake()Empêche les notifications Storage::fake('disk')Filesystem fictif Http::fake([...])Requêtes HTTP externes Exceptions::fake()Capture les exceptions