Comment faire les test unitaires d'une API Laravel avec Pest

February 18, 2021
Rédigé par
Dotun Jolaoso
Contributeur
Les opinions exprimées par les contributeurs de Twilio sont les leurs
Révisé par
Diane Phan
Twilion

Comment faire les test unitaires d'une API Laravel avec le framework Pest

Pest est un nouveau framework de test PHP développé par Nuno Maduro. Bien que Pest lui-même soit construit sur PHPUnit, le framework populaire et largement adopté de test PHP, Pest vise à fournir une meilleure expérience pour écrire des tests. La philosophie est simple. Faites en sorte que l'expérience TDD soit simple et élégante en fournissant des interfaces expressives.

Dans ce tutoriel, nous allons nous familiariser avec l'utilisation de Pest dans un projet Laravel. En nous basant sur l'approche axée sur les tests, nous allons créer une application simple qui vous permettra de créer, modifier, mettre à jour et supprimer des tâches.

Exigences techniques

  • PHP version 7.3 ou supérieure. PEST nécessite PHP 7.3+ pour fonctionner.
  • Laravel 8.
  • Composer.
  • Une compréhension de base de PHPUnit.
  • Une compréhension de base de SQLite. Nous allons utiliser SQLite, car il accélère l'exécution de nos tests.

Configurer Laravel

Il existe différentes façons de configurer un nouveau projet Laravel. Vous pouvez le faire via le programme d'installation Laravel ou en utilisant Composer. Dans le cadre de ce tutoriel, nous allons utiliser Composer.

Exécutez la commande suivante dans votre terminal :

$ composer create-project --prefer-dist laravel/laravel pest-todo

Cela va créer un projet Laravel pour nous dans le répertoire pest-todo.

Installer Pest

Maintenant que nous disposons d'un nouveau projet Laravel, nous avons besoin de quelques étapes supplémentaires pour installer Pest avec Laravel. Saisissez cd pest-todo pour passer dans le nouveau répertoire pest-todo, puis exécutez la commande suivante :

$ composer require pestphp/pest --dev

Ensuite, nous allons installer le plug-in Pest pour Laravel. Pour ce faire, exécutez la commande suivante :

$ composer require --dev pestphp/pest-plugin-laravel

Une fois le plug-in installé, exécutez la commande suivante :

$ php artisan pest:install

Ceci créera un fichier Pest.php dans le répertoire tests. Le fichier Pest.php est chargé automatiquement et sert d'emplacement idéal pour lier de manière récursive des classes et des traits d'aide à vos tests. Laravel est fourni avec des exemples de fichiers de test basés sur PHPUnit. Modifions ces tests pour utiliser Pest à la place. Rendez-vous dans le répertoire tests/Feature et consultez le fichier ExampleTest.php. Voici à quoi cela ressemble actuellement :

 

<?php

namespace Tests\Feature;

use Tests\TestCase;

class ExampleTest extends TestCase
{
    /**
     * A basic test example.
     *
     * @return void
     */
    public function testBasicTest()
    {
        $response = $this->get('/');
        $response->assertStatus(200);
    }
}

Pour migrer ce test vers l'implémentation Pest correspondante, remplacez le contenu du fichier par le code suivant :

 

<?php

it('has welcome page')->get('/')->assertStatus(200);

C'est beaucoup plus propre, n'est-ce pas ? Nous avons réduit ce fichier ExampleTest d'environ 20 lignes de code à seulement 2 lignes tout en testant exactement la même chose et en produisant le même résultat. C'est-à-dire qu'il visite l'URL racine à l'emplacement '/' et affirme qu'un code d'état HTTP de 200 est renvoyé.

Pest fournit deux fonctions pour écrire des tests - test() et it(). Les deux fonctions acceptent une description de test comme premier argument et une clôture qui contient les attentes de test comme deuxième argument. Elles partagent la même syntaxe et le même comportement, et vous êtes libre d'utiliser celle que vous trouvez appropriée. Personnellement, je préfère utiliser it(), cette fonction permettant de lire les cas de test comme une phrase complète.

De même, faisons en sorte que le fichier ExampleTest.php situé dans le répertoire tests/Unit utilise Pest également. Remplacez le contenu du fichier par le code suivant :

 

<?php

test('basic')->assertTrue(true);

Utilisez ensuite la commande suivante pour exécuter la suite de tests :

$ ./vendor/bin/pest

Tous les tests doivent réussir comme indiqué sur l'image ci-dessous.

tests réussis

Créer le modèle, la migration et le contrôleur to-do

Notre application va avoir un modèle unique appelé Todo. Laravel fournit une commande pratique pour générer simultanément un modèle, une migration et un contrôleur pour une entité. Pour ce faire, exécutez la commande suivante :

$ php artisan make:model Todo -m -c

Un nouveau fichier migration est créé dans le répertoire database/migrations à l'emplacement [TODAYSDATE]_create_todos_table.php. Ajoutez ensuite le code suivant à la méthode up() dans le fichier de migration :

 $table->string('name');
 $table->boolean('completed')->default(false);

Chaque tâche to-do aura un attribut name, ainsi qu'un attribut booléen completed avec une valeur par défaut false. Ensuite, modifiez le fichier App/Models/Todo.php avec le code suivant :

 

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Todo extends Model
{
    use HasFactory;

    protected $fillable = ['name', 'completed'];
}

Ici, nous attribuons les attributs name et completed du modèle pour qu'ils puissent être affectés en masse.

Créer la fabrique (factory) to-do

Les fabriques de modèles de Laravel offrent un moyen pratique pour semer des données dans la base de données. Ceci est très utile lorsqu'il s'agit de tests. Exécutez la commande suivante pour créer une classe de fabrique (factory) pour le modèle to-do :

$ php artisan make:factory TodoFactory

Cela créera TodoFactory.php pour nous dans le répertoire database/factories. Modifiez la méthode definition() dans le fichier pour renvoyer un tableau similaire à celui ci-dessous :

return [
    'name' => 'Deploy Twilio Verify to Live',
    'completed' => false
];

La méthode definition renvoie l'ensemble par défaut des valeurs d'attribut qui doivent être appliquées lors de la création d'un modèle à l'aide de la fabrique.

Configurer la base de données

Nous allons utiliser une base de données SQLite en mémoire pour les tests. Nos tests seront ainsi exécutés plus rapidement. Laravel prend déjà en charge l'utilisation d'une base de données SQLite pour les tests. Accédez au fichier phpunit.xml situé à la racine du répertoire de votre projet et supprimez les commentaires des lignes de code suivantes :

<server name="DB_CONNECTION" value="sqlite"/>
<server name="DB_DATABASE" value=":memory:"/>

Écrire les tests

Maintenant que nous avons effectué toutes les configurations nécessaires, nous pouvons commencer à écrire les tests. Ces tests sont nécessaires pour que l'application fonctionne et fournissent la mise en œuvre correspondante pour s'assurer que tous les tests réussissent.

Exécutez la commande Pest suivante pour créer un fichier de test unitaire :

$ php artisan pest:test TodoTest --unit

Cela créera TodoTest.php dans le répertoire test/Unit. Remplacez le code du fichier par le code suivant :

 

<?php

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

uses(Tests\TestCase::class, RefreshDatabase::class);

it('does not create a to-do without a name field', function () {
    $response = $this->postJson('/api/todos', []);
    $response->assertStatus(422);
});

it('can create a to-do', function () {
    $attributes = Todo::factory()->raw();
    $response = $this->postJson('/api/todos', $attributes);
    $response->assertStatus(201)->assertJson(['message' => 'Todo has been created']);
    $this->assertDatabaseHas('todos', $attributes);
});

it('can fetch a to-do', function () {
    $todo = Todo::factory()->create();

    $response = $this->getJson("/api/todos/{$todo->id}");

    $data = [
        'message' => 'Retrieved To-do',
        'todo' => [
            'id' => $todo->id,
            'name' => $todo->name,
            'completed' => $todo->completed,
        ]
    ];

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

it('can update a to-do', function () {
    $todo = Todo::factory()->create();
    $updatedTodo = ['name' => 'Updated To-do'];
    $response = $this->putJson("/api/todos/{$todo->id}", $updatedTodo);
    $response->assertStatus(200)->assertJson(['message' => 'To-do has been updated']);
    $this->assertDatabaseHas('todos', $updatedTodo);
});

it('can delete a to-do', function () {
    $todo = Todo::factory()->create();
    $response = $this->deleteJson("/api/todos/{$todo->id}");
    $response->assertStatus(200)->assertJson(['message' => 'To-do has been deleted']);
    $this->assertCount(0, Todo::all());
});

En haut du fichier, la méthode uses() lie la classe TestCase et le trait RefreshDatabase au fichier de test en cours. La classe de base TestCase est fournie par Laravel et propose des méthodes d'aide pour travailler avec le framework pendant les tests. Le trait RefreshDatabase prend en charge la migration et la réinitialisation de la base de données après chaque test afin que les données d'un test précédent n'interfèrent pas avec les tests suivants.

Maintenant, examinons ce que chaque test fait :

  • it(‘does not create a to-do without a name field’)  : Laravel fournit plusieurs assistants pour tester les API JSON et leurs réponses. Ici, nous utilisons l'aide postJson pour créer une requête POST au point de terminaison api/todos passant dans un tableau vide. Ensuite, la méthode assertStatus() sur la réponse renvoyée garantit qu'un code d'état HTTP de 422 doit être renvoyé. Ce test garantit qu'un champ de nom sera toujours présent sur la charge utile de la requête.
  • it(‘can create a to-do’)  : ce test garantit qu'une tâche to-do est créée lors d'une requête POST au point de terminaison api/todos. Nous déclarons qu'un code d'état HTTP de 201 est renvoyé et que la base de données contient la tâche to-do à l'aide de la méthode assertDatabase().
  • it(‘can fetch a to-do’) : ce test vérifie qu'une tâche to-do particulière peut être récupérée au moyen de l'ID. À l'aide de la méthode create() de la fabrique Todo, une tâche to-do est créée et stockée dans la base de données. De même, nous déclarons que le code d'état renvoyé est 200. La méthode assertJson() convertit la réponse en tableau et vérifie que le tableau donné existe dans la réponse JSON renvoyée par l'application.
  • it(‘can update a to-do’) : ce test garantit qu'une tâche to-do peut être mise à jour et que la tâche mise à jour peut être trouvée dans la base de données.
  • it(‘can delete a to-do’)  : ce test garantit qu'une tâche to-do peut être supprimée et vérifie que le nombre total de tâches contenues dans la base de données est zéro.

Pour exécuter la suite de tests, exécutez la commande suivante :

$ ./vendor/bin/pest --filter TodoTest

La suite de tests devrait échouer, car nous n'avons mis en œuvre aucune des fonctions.

erreurs au lancement de la suite de tests

Créer l'application TO-DO

Donnons l'implémentation correspondante des tests que nous avons rédigés jusqu'à présent. Accédez au fichier TodoController.php dans le répertoire app/Http et remplacez le code du fichier par le code suivant :

 

<?php

namespace App\Http\Controllers;

use App\Models\Todo;
use Illuminate\Http\Request;

class TodoController extends Controller
{
    public function create(Request $request)
    {
        $request->validate($this->rules());

        $todo = Todo::create($request->only(['name']));

        $data = [
            'message' => 'To-do has been created',
            'todo' => $this->mapTodoResponse($todo)
        ];

        return response()->json($data, 201);
    }

    public function show(Todo $todo)
    {
        $data = [
            'message' => 'Retrieved To-do',
            'todo' => $this->mapTodoResponse($todo)
        ];

        return response()->json($data);
    }

    public function update(Todo $todo, Request $request)
    {
        $request->validate($this->rules());

        $todo->update($request->only(['name']));
        $todo->refresh();

        $data = [
            'message' => 'To-do has been updated',
            'todo' => $this->mapTodoResponse($todo)
        ];

        return response()->json($data);
    }

    public function delete(Todo $todo)
    {
        $todo->delete();

        $data = [
            'message' => 'To-do has been deleted'
        ];

        return response()->json($data);
    }

    protected function rules()
    {
        return [
            'name' => 'required|string|min:4'
        ];
    }

    protected function mapTodoResponse($todo)
    {
        return [
            'id' => $todo->id,
            'name' => $todo->name,
            'completed' => $todo->completed
        ];
    }
}
  • La méthode create() crée une nouvelle tâche to-do.
  • La méthode show() renvoie une tâche donnée en fonction de son ID.
  • La méthode update() met à jour une tâche to-do.
  • La méthode delete() supprime une tâche to-do donnée.

Ajoutez ensuite les chemins suivants au fichier routes/api.php :

 

<?php

Route::get('/todos/{todo}', 'App\Http\Controllers\TodoController@show');
Route::post('/todos', 'App\Http\Controllers\TodoController@create');
Route::put('/todos/{todo}', 'App\Http\Controllers\TodoController@update');
Route::delete('/todos/{todo}', 'App\Http\Controllers\TodoController@delete');

Maintenant que nous avons fourni toutes les implémentations correspondantes pour les tests, nous pouvons revenir à l'exécution de nos tests et ils devraient tous réussir maintenant. Exécutez la suite de tests à l'aide de la commande suivante :

$ ./vendor/bin/pest --filter TodoTest

tous les tests passent

Conclusion

Dans ce tutoriel, nous avons vu comment écrire des tests unitaires pour une application Laravel à l'aide du framework de test Pest. Ce tutoriel peut servir de guide pratique pour commencer à utiliser Pest ainsi que pour tester une application Laravel. Le référentiel GitHub avec le code complet de ce projet est disponible ici.

Site Web : https://dotunj.dev/
Github : https://github.com/Dotunj
Twitter : https://twitter.com/Dotunj_