Comment faire des tests unitaires sur une API rest avec Laravel 9 et PEST
Published: 2022-08-01 15:39:00

Laravel est un framework réputé pour  l’élégance au niveau de sa syntaxe et sa facilité d’apprentissage, il est utilisé par les artisans du web pour le développement d’applications simples mais aussi complexes. Dans le développement d’applications web une des bonnes pratiques est de tester son code en écrivant du code pour vérifier si l’application fonctionne comme prévu.

Dans ce tutoriel nous allons concevoir une API REST pour l’authentification des utilisateurs(Register, login, logout) et mettre en place une série de tests afin de vérifier si notre code fonctionne comme nous le souhaitons. Il faut noté qu’il est possible de tester une telle application en utilisation un outil comme POSTMAN pour observer les réponses d’api, ce n’est pas de ça qu’il s’agit ici.

Pour mettre en place nos tests nous utiliserons un framework de test nommé PEST qui lui aussi à l’instar de Laravel propose une syntaxe élégante et facile à comprendre il a été  conçu pour apporter la joie des tests à PHP.

Note: Prérequis pour suivre ce tutoriel

Connaitre le framework Laravel (Créaction de model, Eloquent, Routage)
Connaitre les bases du fonctionnement des API REST et avoir déjà implémentée une API REST avec laravel
Savoir installer des packages externes dans une application laravel via le gestionnaire COMPOSER
Okay trêve de bavardage on rentre dans le vif du sujet, commence d’abord par créer et mettre en place un projet laravel.

1-Installation de Laravel et Laravel Passport

Créer un nouveau projet laravel utilisait la commande suivante et installer le package PASSPORT de larve en suivant les étapes ci-dessous.

`composer create-project laravel/laravel lara-test-api `

Une fois l’installation terminée crée une base de donné et ajouté l’eau projet grâce au fichier .env de configuration. Notre objectif est la création d’un système d’authentification basé sur les tokens  JWT(json web tokens) pour cela nous allons utiliser une librairie Laravel  nommé PASSPORT qui va grandement faciliter notre travail pour la créaction des tokens.

Taper cette commande à la racine de votre application laravel créée précédemment cela permettra de télécharger le package Passport sur notre projet

`composer require laravel/passport`

Ensuite vous devez exécuter les migrations avec la commande suivante pour mettre à jour votre base de donner.

`php artisan migrate`

Ensuite, vous devez exécuter la commande  php artisan passport:install. Cette commande créera les clés de cryptage nécessaires pour générer des jetons d’accès sécurisés.

 `php artisan passport:install`

Dans le modèle utilisateur vous devez supprimer le trait Laravel\Sanctum\HasApiTokens  venu par défaut lors de l’installation et le remplacer par Laravel\Passport\HasApiTokens qui lui viens comme vous pouvez le constater avec Passport. Nous aurons donc ceci dans le fichier User.php.

```

namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Passport\HasApiTokens;
class User extends Authenticatable
{
use HasApiTokens, HasFactory, Notifiable;

/**
* The attributes that are mass assignable.
*
* @var array<int, string>
*/
protected $fillable = ['name','email','password',];

/**
* The attributes that should be hidden for serialization.
*
* @var array<int, string>
*/
protected $hidden = ['password','remember_token',];

/**
* The attributes that should be cast.
*
* @var array<string, string>
*/
protected $casts = ['email_verified_at' => 'datetime',];
}

```
Nous avons désormais un projet laravel avec laravel qui peut créer des tokens jwt pour l’authentification des requêtes d’API.

2- Création de l’API rest d’authentification.
Notre application a désormais tous ce qu’il faut pour créer et envoyer des tokens JWT, dans cette partie nous allons créer les différentes API pour la création de compte(register), la connexion au compte(login), la déconnexion d’un compte(logout).

Créer donc un contrôleur nommé AuthenticationController avec la commande artisan ci-dessous.

`php artisan make:controller Api/v1/Auth/AuthenticationController`

Dans se contôleur nous allons écrire la logique de nos fonctions (Register, login, logout), à cet effet nous allons commencer par le fonction register qui va nous permettre de créer un compte pour un utilisateur avec les informations suviantes

Email
Nom
Mot de passe
Donc notre fonction register se présente comme suite:


```
public function register(Request $request) {
// Validation des donnés envoyés par l'utilisateur
  $request->validate([
     'name' => 'required|string|max:50',
     'email' => 'required|email|max:50',
     'password'=>'required|string|min:6',
  ]);

$user = User::create([
   "email" => $request->email,
   'name' => $request->name,
   'password' => bcrypt('password')
]);

// Créaction et recupération du token jwt
$data['token'] = $user->createToken($user->email)->accessToken;
$data['message'] = 'Utilisateur crée';

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

La fonction login comme vous le savez sans doute déjà aura pour rôle de connecter un utilisateur déjà inscript à notre site en utilisant l’email et le mot de passe, ajouter le code ci-dessous à votre contrôleur


```
public function login(Request $request) {
   $request->validate([
      'email' => 'required|string|max:20',
      'password'=>'required|string|min:6',
   ]);

   $credentials = $request->only(['email', 'password']);
   if(Auth::attempt($credentials)){
      $data['token'] = Auth::user()
            ->createToken($request->email)->accessToken;
      $data['message'] = 'Connexion réussie';

      return response()->json($data,200);
    }
    $data['message'] = "Vos identifiants sont 
     incorrects.";

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


```
Notre fonction logout va nous permettre de déconnecter un utilisateur en supprimant le token authentification créer lors de la connexion, ajouter donc ce code à votre contrôleur.


```
public function logout(Request $request) {
     Auth::user()->token()->revoke();
     $data['message'] = 'Déconnexion réussie.';

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

Créons maintenant les routes dans le fichier api.php afin de disposer d’urls à pour faire nos appels api. Ajouter le code ci-dessous à votre fichier api.php dans le dossier routes.


```
use App\Http\Controllers\Api\v1\Auth\AuthenticationController;
use Illuminate\Support\Facades\Route;
Route::group(['prefix' => 'v1'], function() {
Route::post('register', [AuthenticationController::class, 'register']);
Route::post('login', [AuthenticationController::class, 'login']);
Route::post('logout', [AuthenticationController::class, 'logout']);
});
```

Commençons par installé le package laravel de pest qui va nous permettre d’écrire les tests pour notre code, De base pest est framework de tests pour PHP, un pluggin a été développer pour prendre en charge le framework laravel pour l’installer dans votre application laravel il suffit de taper le commande suivante;

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

Après le téléchargement exécuté la commande d’installation ci-dessous, cette commande installe le package et crée un fichier nommé Pest.php dans le dossier tests de votre application
`
php artisan pest:install`

Modifier la ligne 14 du fichier Pest.php comme ceci
uses(Tests\TestCase::class)->in('Unit');

Nos tests seront contenus dans un fichier, utilisons la commande artisan pest:test <name> pour créer un fichier dans le dossier tests/Unit,

php artisan pest:test AuthenticationTest --unit

le drapeau –unit  précise qu’il s’agit d’un test unitaire à la différence des tests  de features, sans ce drapeau la commande artisan ci-dessus génère le fichier  AuthenticationTest.php  dans le dossier tests/Feature.

La fonction register créer un utilisateur et renvoie une réponse au format json en utilisation le verbe HTTP post, ajoute le code ci-dessous dans le fichier tests/Unit/AuthenticationTest.php

```
use App\Models\User;
use function Pest\Laravel\postJson;

test('User can register', function () {
    $user = User::factory()->raw();
    $response = postJson('api/v1/register', $user);
    $response->assertStatus(200)
        ->assertJsonStructure(['message', 'token']);
});

```
Explications:

Nous importons d’abord le modèle utilisateur et la fonction postJson qui va nous permettre de soumettre une requête post avec les donnés requits pour la création de compte.
La fonction test prend deux arguments représentant respectivement le nom de notre test (User can register) et une function qui contient les attentes du test.
La fonction postJson prend deux paramètres (l’url et les données à envoyer dans notre cas la variable $user qui utilise les factory pour avoir un utilisateur. postJson renvoi un objet sur lequel nous pouvons appeler des méthodes telques assertStatus et assertJsonStructure, d’autres méthodes d’assertion sont disponibles sur l’objet $response instance de la class  Illuminate\Testing\TestResponse. 
assertStatus vérifie le status de la requête effectuée par le méthode postJson dans notre cas ici il va vérifier si le status de la réponse est égale à 200.
assertJsonStructure vérifie la structure des donnés json renvoyé, plus précisement si il y à une clé ‘data’.
Pour exécuter notre  tests taper la commande suivante

`php artisan test --testsuite=Unit --filter=AuthenticationTest`

la commande ci-dessus précise le résultat du test en terminale comme ceci:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/60wcvauetvutut7qh9ws.png)

Testons les autres fonctions, ajouter le code ci-dessous dans votre fichier AuthenticationTest.php

Fonction login 

```

test('User can login', function (){
     $userCredential = [
       'email' => User::first()->email, 
       'password'=>'password'
      ];
      $response = postJson('api/v1/login', $userCredential);
     $response->assertStatus(200)
       ->assertJsonStructure(['message', 'token']);
});

```
Fonction login avec échec de connexion

```

test('User can not login', function() {
     $userCredential = [
        'email' => User::first()->email, 
        'password'=>'pasdsdssd'
     ];
     $response = postJson('api/v1/login', $userCredential);
    $response->assertStatus(401)
         ->assertJsonStructure(['message']);
});
```

Fonction logout

```
test('User logout', function(){
    $userLoged = postJson('api/v1/login', [
        'email' => User::first()->email,
        'password' => 'password',
    ]);
    // Get user token in requette response
    $userToken = $userLoged['token'];
    $response = withHeader('Authorization', 'Bearer '.$userToken)->json('post','api/v1/logout');
    $response->assertStatus(200)->assertJsonStructure(['message']);
});
```

Vérifions si tous nos tests passent en exécutant de nouveau la commande :

`php artisan test --testsuite=Unit --filter=AuthenticationTest`

Voici le résultat si tous ce passe bien

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/p0xc0tihmnecdchnu3i4.png)

On remarque dans la console que tous nos tests passent, Nous voici avec une application simple testée.