Aller au contenu

CI - Tests unitaires đŸ§Ș⚓

L’exĂ©cution de tests unitaires et d’intĂ©grations est au coeur de la dĂ©marche DevOps et d’intĂ©gration continue. C’est eux qui vont permettre de dĂ©tecter les anomalies dans les dĂ©veloppements rĂ©alisĂ©s. Github Actions, si un ou plusieurs tests sont en erreur, va fournir les rapports dĂ©taillĂ©s permettant d’identifier l’origine de l’erreur, et va signaler que la tĂąche est en erreur. Cela peut par exemple empĂȘcher un merge de se rĂ©aliser dans Github.

📋 Texte
- name: Run tests with coverage
  env: 
    XDEBUG_MODE: coverage 
    run: php artisan test --coverage --min=80

🎯 Objectifs pĂ©dagogiques

  • Comprendre la structure des tests Unit et Feature dans Laravel.
  • Écrire, exĂ©cuter et interprĂ©ter des tests automatisĂ©s.
  • IntĂ©grer les tests dans une pipeline d’intĂ©gration continue (CI).
  • Mesurer la couverture de code et fixer un seuil minimal de qualitĂ©.

1. Initialisation de l’environnement de test đŸ—ïžâš“ïžŽ

Crée un environnement dédié aux tests.

đŸ’» Console
cp .env .env.testing
php artisan key:generate

▶ Configure .env.testing ⚙

D'abord la base de données de Test :

🐬 SQL
CREATE DATABASE todo_test;
CREATE USER 'laravel_test'@'localhost' IDENTIFIED BY 'secret';
GRANT ALL PRIVILEGES ON todo_test.* TO 'laravel_test'@'localhost';
FLUSH PRIVILEGES;
puis la configuration

📋 Texte
DB_CONNECTION=mysql 
DB_HOST=127.0.0.1 
DB_PORT=3306 
DB_DATABASE=todo_test 
DB_USERNAME=laravel_test 
DB_PASSWORD=secret
Fix problĂšm đŸȘČ sqlite

Par dĂ©faut, Laravel utilise SQLite pour l’exĂ©cution des tests. Cette base, entiĂšrement en mĂ©moire, est trĂšs performante et particuliĂšrement adaptĂ©e aux tests unitaires. Cependant, SQLite gĂšre mal certaines fonctionnalitĂ©s avancĂ©es, notamment les clĂ©s primaires composites que nous avons mises en place dans nos migrations.

Pour garantir un comportement cohĂ©rent entre l'environnement de dĂ©veloppement, l'environnement de test et l'environnement de production, nous faisons donc le choix d’utiliser MySQL Ă©galement pour les tests.

Dans la continuitĂ©, nous provisionnerons un service MySQL via Docker au sein de GitHub Actions afin d’exĂ©cuter automatiquement la suite de tests dans un environnement reproductible et compatible avec nos contraintes de schĂ©ma.

dans le fichier phpunit.xml Ă  la racine du projet

📋 Texte
  <!--
  <server name="DB_CONNECTION" value="sqlite"/>
  <server name="DB_DATABASE" value=":memory:"/>
  -->
  <server name="DB_CONNECTION" value="mysql"/>
  <server name="DB_DATABASE" value="todo_test"/>
  <server name="DB_USERNAME" value="laravel_test"/>
  <server name="DB_PASSWORD" value="secret"/>

2. Premier test “Smoke test” đŸ§©âš“ïžŽ

CrĂ©e un test pour vĂ©rifier que la page d’accueil est accessible.

đŸ’» Console
php artisan make:test AccueilTest

Cela créé un nouveau répertoire tests, modifions à présent tests/Feature/AccueilTest.php :

🐘 PHP
<?php
public function test_invite_est_redirige_depuis_accueil_vers_login()
{
    $response = $this->get('/');
    $response->assertRedirect(route('login'));
}

public function test_utilisateur_auth_peut_acceder_a_l_accueil()
{
    $user = User::factory()->create();

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

    $response->assertOk();
    // On vérifiera qu'on voit bien le texte "Ma Todo List" dans la page
    $response->assertSee('Ma Todo List');
}
  • assertRedirect(route('login')) montre le comportement pour un invitĂ©.
  • actingAs($user) + followingRedirects() illustre le scĂ©nario d’un utilisateur authentifiĂ©.
  • L’assertion porte sur un texte rĂ©el de la vue, pas sur le nom de la route.

🏃 Lançons les tests :

đŸ’» Console
php artisan test
Fix problĂ©m đŸȘČ
  • Mettre en commentaire le contenu de exampleTest.php
  • RĂ©parer le test d'authentification
đŸ’» Console
FAILED  Tests\Feature\Auth\AuthenticationTest > users can authenticate using the login screen
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-'http://localhost/dashboard'
+'http://localhost'
+++ Actual
@@ @@
-'http://localhost/dashboard'
+'http://localhost'
@@ @@
-'http://localhost/dashboard'
+'http://localhost'
-'http://localhost/dashboard'
+'http://localhost'
+'http://localhost'
résultats
đŸ’» Console
(base) PS C:\wamp64\www\todo2026> php artisan test

PASS  Tests\Unit\ExampleTest
✓ that true is true                                                                                                           0.02s  

PASS  Tests\Feature\AccueilTest
✓ invite est redirige depuis accueil vers login                                                                               0.63s  
✓ utilisateur auth peut acceder a l accueil                                                                                   0.58s  

PASS  Tests\Feature\Auth\AuthenticationTest
✓ login screen can be rendered                                                                                                3.22s  
✓ users can authenticate using the login screen                                                                               0.17s  
✓ users can not authenticate with invalid password                                                                            0.29s  
✓ users can logout                                                                                                            0.10s  

PASS  Tests\Feature\Auth\EmailVerificationTest
✓ email verification screen can be rendered                                                                                   0.14s  
✓ email can be verified                                                                                                       0.08s  
✓ email is not verified with invalid hash                                                                                     0.10s  

PASS  Tests\Feature\Auth\PasswordConfirmationTest
✓ confirm password screen can be rendered                                                                                     0.12s  
✓ password can be confirmed                                                                                                   0.07s  
✓ password is not confirmed with invalid password                                                                             0.30s  

PASS  Tests\Feature\Auth\PasswordResetTest
✓ reset password link screen can be rendered                                                                                  0.07s  
✓ reset password link can be requested                                                                                        0.27s  
✓ reset password screen can be rendered                                                                                       0.38s  
✓ password can be reset with valid token                                                                                      0.33s  

PASS  Tests\Feature\Auth\PasswordUpdateTest
✓ password can be updated                                                                                                     0.08s  
✓ correct password must be provided to update password                                                                        0.08s  

PASS  Tests\Feature\Auth\RegistrationTest
✓ registration screen can be rendered                                                                                         0.07s  
✓ new users can register                                                                                                      0.07s  

PASS  Tests\Feature\ProfileTest
✓ profile page is displayed                                                                                                   0.19s  
✓ profile information can be updated                                                                                          0.07s  
✓ email verification status is unchanged when the email address is unchanged                                                  0.08s  
✓ user can delete their account                                                                                               0.08s  
✓ correct password must be provided to delete account                                                                         0.10s  

Tests:    26 passed (64 assertions)
Duration: 8.40s

3. Les factories Eloquent đŸ­âš“ïžŽ

đŸ§© Qu’est-ce qu’une Factory Laravel ?

Une factory est un outil fourni par Laravel pour générer automatiquement des données de test réalistes.

Elle permet de créer facilement :

  • des objets Eloquent cohĂ©rents (User, Todo, Liste, etc.),
  • avec des valeurs alĂ©atoires mais crĂ©dibles (texte, dates, boolĂ©ens
),
  • et surtout sans Ă©crire manuellement chaque champ.

Les factories sont essentielles pour les tests, car elles permettent :

  • de simplifier la crĂ©ation d’entrĂ©es en base,
  • d’éviter de dupliquer du code,
  • d’isoler les tests (chaque test crĂ©e ses propres donnĂ©es),

et d’obtenir des tests prĂ©cis, reproductibles et rapides.

Les fichiers de factories se trouvent dans le dossier database/factories/

note : Laravel fournit déjà une factory pour User : UserFactory.php.

3.1. VĂ©rifier la factory User đŸ§‘â€đŸ’»âš“ïžŽ

Ouvre le fichier suivant : database/factories/UserFactory.php

Tu dois y trouver une méthode definition() qui ressemble à ceci :

🐘 PHP
<?php
public function definition(): array
{
    return [
        'name' => fake()->name(),
        'email' => fake()->unique()->safeEmail(),
        'email_verified_at' => now(),
        'password' => '$2y$10$'.strtr('password-hash', ['.' => '/']), // ou Hash::make(...)
        'remember_token' => Str::random(10),
    ];
}

L’important pour nous est :

👉 User::factory()->create() permet de crĂ©er facilement un utilisateur de test valide.

3.2. CrĂ©er une factory pour Listes đŸ“‚âš“ïžŽ

On veut pouvoir gĂ©nĂ©rer des listes de Todos pour les tests (ex. “Maison”, “Travail”, etc.).

Crée la factory : php artisan make:factory ListesFactory --model=Listes

Puis complĂšte database/factories/ListesFactory.php :

🐘 PHP
<?php
use App\Models\Listes;
use Illuminate\Database\Eloquent\Factories\Factory;

class ListesFactory extends Factory
{
   protected $model = Listes::class;

    public function definition(): array
    {
        return [
            'titre' => fake()->words(2, true), // par ex. "Maison", "Bureau perso"
        ];
    }

}

💡 Explication ListeFactory

Ici, on utilise fake()->words(2, true) pour générer un nom de liste avec 2 mots.
À partir de lĂ , un simple Listes::factory()->create() dans un test insĂ©rera une ligne dans la table listes avec un champ titre cohĂ©rent.

A faire

✅ CrĂ©er une factory pour Todos (sans relations)

🐘 PHP
<?php

namespace Database\Factories;

use Illuminate\Database\Eloquent\Factories\Factory;
use App\Models\Todos;
use App\Models\Listes;
use App\Models\User;    

/**
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Todos>
*/
class TodosFactory extends Factory
{
    protected $model = Todos::class;

    public function definition(): array
    {
        return [
            'texte'     => fake()->sentence(3),
            'termine'   => false,
            'important' => false,
            'date_fin'  => null,
            'listes_id' => Listes::factory(), // crée une Liste associée
            'user_id'   => User::factory(),  // crée un User associé
        ];
    }
}

Avec cette factory, on peut déjà écrire :

🐘 PHP
<?php
$todos = Todos::factory()->create();
Laravel créera un enregistrement dans la table todos avec :

  • un texte rĂ©aliste,
  • termine = false,
  • important = false,
  • les FK listes_id et user_id Ă  null (non associĂ©es pour l’instant).

C’est suffisant pour des premiers tests sur les attributs du modùle Todo.

3.3. VĂ©rifier le bon fonctionnement des factories đŸŽąâš“ïžŽ

Tinker est une console interactive fournie par Laravel, basĂ©e sur PsySH, qui permet d’exĂ©cuter du code PHP en direct au sein de ton application.

C’est un outil extrĂȘmement pratique pour :

  • tester rapidement des modĂšles Eloquent,
  • manipuler la base de donnĂ©es depuis le shell,
  • vĂ©rifier le fonctionnement d’une factory,
  • expĂ©rimenter un morceau de logique mĂ©tier,
  • dĂ©boguer un comportement sans Ă©crire de route ni de test.

▶ Ouvrir Tinker

Dans ton terminal (Ă  la racine du projet) : php artisan tinker

Tu obtiens alors une console interactive :

📋 Texte
Psy Shell v0.11.8 (PHP 8.2 
)
>>>
Tout ce que tu écris ici est exécuté dans le contexte de ton application Laravel.

Fix problĂšme đŸȘČ
  • penser Ă  ajouter use HasFactory; dans le model de Todos
  • Vider le cache Laravel
đŸ’» Console
php artisan optimize:clear
composer dump-autoload

3.4. Todo liĂ© Ă  une Liste et Ă  un User đŸ”—âš“ïžŽ

Pour des tests plus complets (relations Eloquent), on peut demander à la factory Todo de créer automatiquement une Liste et un User associés.

code TodoFactory
🐘 PHP
<?php
use App\Models\Todos;
use App\Models\Listes;
use App\Models\User;
use Illuminate\Database\Eloquent\Factories\Factory;

class TodosFactory extends Factory
{
    protected $model = Todos::class;

    public function definition(): array
    {
        return [
            'texte'     => fake()->sentence(3),
            'termine'   => false,
            'important' => false,
            'date_fin'  => null,
            'listes_id' => Listes::factory(), // crée une Liste associée
            'user_id'   => User::factory(),  // crée un User associé
        ];
    }
}

En configurant listes_id et user_id avec Liste::factory() et User::factory(), on obtient un comportement trĂšs puissant :

🐘 PHP
<?php
$todo = Todos::factory()->create();

réalise en réalité :

  1. crĂ©ation d’un User de test,
  2. crĂ©ation d’une Listes de test,
  3. création du Todos lié à ces deux enregistrements.

Cela simplifie énormément les tests qui portent à la fois sur Todos et sur ses relations (ex. todos->listes, todos->user).

✔ Tester une factory

đŸ’» Console
> App\Models\Listes::factory()->create();
= App\Models\Listes {#6211
    titre: "eos ut",
    updated_at: "2025-12-06 13:09:32",
    created_at: "2025-12-06 13:09:32",
    id: 4,
  }

Résultat : un nouvel enregistrement todos est inséré dans la base de développement.

✔ CrĂ©er un utilisateur

đŸ’» Console
> App\Models\User::factory()->create();
= App\Models\User {#6632
    name: "Diane Lambert",
    email: "pauline27@example.net",
    email_verified_at: "2025-12-06 13:14:50",
    #password: "$2y$12$mynJ8kscphRfl1KCdRRVR.XS8hKU0dxzQvaBkTWWc8P9TkgonEgEq",
    #remember_token: "nNiqZwe2AN",
    updated_at: "2025-12-06 13:14:51",
    created_at: "2025-12-06 13:14:51",
    id: 3,
  }

✔ Tester la factory Todos

đŸ’» Console
> App\Models\Todos::factory()->create();
= App\Models\Todos {#5520
    texte: "Ut rerum praesentium fugit.",
    termine: false,
    important: false,
    date_fin: null,
    listes_id: 5,
    user_id: 4,
    updated_at: "2025-12-06 13:22:15",
    created_at: "2025-12-06 13:22:15",
    id: 9,
  }

✔ Lister les enregistrements existants : App\Models\Todos::all();

4. Tests Unitaires : modĂšle Eloquent đŸ§±âš“ïžŽ

Dans cette Ă©tape, nous allons vĂ©rifier le comportement du modĂšle Todos Ă  l’aide de tests unitaires :

  • relations Todos → Listes et Todos → User,
  • valeurs par dĂ©faut de certains attributs (termine, important, etc.).

L’objectif est de valider le modùle sans passer par les routes ni les vues.

4.1 CrĂ©ation du test de modĂšle đŸ§Ș⚓

GénÚre un test unitaire dédié au modÚle Todos :

đŸ’» Console
php artisan make:test TodosModelTest --unit
Cela crée le fichier tests/Unit/TodosModelTest.php

Modifier l’en-tĂȘte du fichier pour qu’il Ă©tende bien la classe de test Laravel (et pas le TestCase brut de PHPUnit), afin de pouvoir utiliser les factories et la base de donnĂ©es de test.

🐘 PHP
<?php

namespace Tests\Unit;

use Tests\TestCase;
use Illuminate\Foundation\Testing\RefreshDatabase;
use App\Models\Todos;
use App\Models\Listes;
use App\Models\User;

class TodosModelTest extends TestCase
{
    use RefreshDatabase;

    // Les tests viendront ici
}
💡 Pourquoi utiliser Tests\TestCase et RefreshDatabase ?
  • Tests\TestCasecharge le contexte Laravel complet (config, Eloquent, etc.) dans les tests, mĂȘme dans tests/Unit.
  • RefreshDatabase relance les migrations pour chaque test afin de garantir une base de test propre (pas de pollution entre tests).

4.2 Tester la relation Todos → Listes đŸ”—âš“ïžŽ

Ton modÚle Todos déclare une relation :

🐘 PHP
<?php
public function listes(): BelongsTo
{
    return $this->belongsTo(Listes::class)->withDefault();
}
Nous allons vérifier que cette relation fonctionne bien.

Dans TodosModelTest :

🐘 PHP
<?php
public function test_un_todos_appartient_a_une_liste()
{
    // Arrange : création d'une liste
    $liste = Listes::factory()->create();

    // Act : création d'un Todos lié à cette liste
    $todo = Todos::factory()->for($liste, 'listes')->create();

    // Assert : la relation renvoie bien la bonne liste
    $this->assertTrue($todo->listes->is($liste));
}
💡 DĂ©tail sur for($liste, 'listes')
  • Listes::factory()->create() crĂ©e une ligne dans la table listes.
  • Todos::factory()->for($liste, 'listes')->create() demande Ă  Laravel de remplir la clĂ© Ă©trangĂšre correspondant Ă  la relation listes dans le modĂšle Todos.
  • L’assertion is() vĂ©rifie que $todo->listes et $liste reprĂ©sentent le mĂȘme enregistrement.

🏃 Relancer les tests : php artisan test

A faire

Tester la relation Todos → User đŸ‘€

🐘 PHP
<?php
public function test_un_todos_appartient_a_un_utilisateur()
{
    // Arrange : création d'un utilisateur
    $user = User::factory()->create();

    // Act : création d'un Todos lié à cet utilisateur
    $todo = Todos::factory()->for($user, 'user')->create();

    // Assert : la relation renvoie bien le bon utilisateur
    $this->assertTrue($todo->user->is($user));
}
💡 Pourquoi tester les relations ?

Ces tests garantissent que :

  • les clĂ©s Ă©trangĂšres (listes_id, user_id) sont correctement utilisĂ©es,
  • les mĂ©thodes listes() et user() renvoient bien les modĂšles attendus.

En cas de renommage de colonne, de modĂšle ou de relation, ces tests serviront de garde-fous.

A faire

Tester les valeurs par dĂ©faut (termine, important) ✅

🐘 PHP
<?php
public function test_un_todos_est_non_termine_et_non_important_par_defaut()
{
    // Act : création d'un Todos sans préciser termine/important
    $todo = Todos::factory()->create();

    // Assert : les valeurs par défaut sont respectées
    $this->assertFalse($todo->termine);
    $this->assertFalse($todo->important);
}   

5.Tests de validation đŸ§źâš“ïžŽ

Nous allons maintenant Ă©crire des tests de validation pour le formulaire d’ajout d’un Todo.

🎯 Objectifs"

  • VĂ©rifier que certains champs sont obligatoires (texte notamment).
  • VĂ©rifier que certains champs respectent des contraintes (longueur minimale, format de date
).
  • VĂ©rifier qu’un Todo valide est bien créé en base.

5.1 PrĂ©parer un test dĂ©diĂ© đŸ“„âš“ïžŽ

Crée un test de type Feature php artisan make:test TodosValidationTest

Cela créé un fichier dans tests/Feature/TodosValidationTest.php, modifier le code ainsi :

🐘 PHP
<?php

namespace Tests\Feature;

use App\Models\User;
use App\Models\Listes;
use App\Models\Todos;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;

class TodosValidationTest extends TestCase
{
    use RefreshDatabase;

    /**
     * Méthode utilitaire pour poster un Todo avec des données par défaut
     */
    private function postTodo(array $overrides = [])
    {
        $user = User::factory()->create();
        $this->actingAs($user);

        $liste = Listes::factory()->create();

        $data = array_merge([
            'texte'      => 'Acheter du café',
            'date_fin'   => now()->addDay()->format('Y-m-d\TH:i'),
            'priority'   => 0,              // bouton radio importance
            'categories' => [],             // tableau de catégories (checkbox)
            'liste'      => $liste->id,     // correspond Ă  $request->input('liste')
        ], $overrides);

        return $this->post('/action/add', $data);
    }
💡 Pourquoi une mĂ©thode postTodo() ?

Cette méthode permet de :

  • centralise la construction d’un jeu de donnĂ©es valide,
  • permet d’écrire des tests plus courts en ne modifiant que les champs Ă  tester,
  • garantit que tous les autres champs restent cohĂ©rents (liste existante, user connectĂ©, etc.).

5.2 texte est obligatoire ✏⚓

Premier cas : le champ texte doit ĂȘtre obligatoire.

🐘 PHP
<?php
public function test_texte_est_obligatoire()
{
    $response = $this->postTodo(['texte' => '']);

    $response->assertSessionHasErrors('texte');
    $this->assertDatabaseCount('todos', 0);
}

question "💡 Comportement attendu cĂŽtĂ© contrĂŽleur ?"

Dans ton contrĂŽleur qui traite /action/add, tu dois avoir quelque chose comme :

🐘 PHP
<?php
$validated = $request->validate([
    'texte' => ['required', 'string'],
    // autres rùgles

]);
Le test vérifie que :

  • si texte est vide, Laravel renvoie une erreur de validation sur ce champ,
  • aucun Todos n’est créé en base.

mais

đŸ’» Console
FAIL  Tests\Feature\TodosValidationTest
  ⚯ texte est obligatoire                                                                                                       0.11s  
  ───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────  
   FAILED  Tests\Feature\TodosValidationTest > texte est obligatoire
  Session is missing expected key [errors].
Failed asserting that false is true.

  at tests\Feature\TodosValidationTest.php:40
     36▕     public function test_texte_est_obligatoire()
     37▕     {
     38▕         $response = $this->postTodo(['texte' => '']);
     39▕
  ➜  40▕         $response->assertSessionHasErrors('texte');
     41▕         $this->assertDatabaseCount('todos', 0);
     42▕     }
     43▕ }
     44▕

Il faut adapter le test au contrîleur actuel ! Le contrîleur, en cas de texte vide, ne renvoie pas d’errors bag, mais :

  • dĂ©clenche une ValidationException,
  • la catch renvoie un redirect()->route('todo.liste')->with('message', "...").

Donc le test doit vérifier :

  • la redirection vers la route,
  • la prĂ©sence du message en session,
  • l’absence de nouvelle ligne dans todos.

🐘 PHP
<?php
public function test_texte_est_obligatoire()
{
    $response = $this->postTodo(['texte' => '']);
    $response->assertRedirect(route('todo.liste'));
    $response->assertSessionHas('message', "Veuillez saisir un ToDo d'une longueur max de 255 caractĂšres");
    $this->assertDatabaseCount('todos', 0);
}
et ca passe beaucoup mieux ...

đŸ’» Console
   PASS  Tests\Feature\TodosValidationTest
  ✓ texte est obligatoire                                                                                                       0.11s  

  Tests:    28 passed (69 assertions)
  Duration: 6.48s

đŸš© Mais quelle est la bonne pratique ?

A faire : longueur minimale

Actuellement, la rĂšgle est 'texte' => 'required|string|max:255', ▶ La rĂšgle doit Ă  prĂ©sent ĂȘtre d'avoir une contrainte supplĂ©mentaire, une longueur minimale de 3 caractĂšres

  • ImplĂ©menter cette rĂšgle
  • Faites en sorte qu'elle soit bien prise en compte dans votre interface
  • Tester cette rĂšgle dans les features de tests.

dans le controleur :

🐘 PHP
<?php
$request->validate([
'texte' => 'required|string|min:3|max:255',
]);
dans les tests :

🐘 PHP
<?php
public function test_texte_doit_avoir_une_longueur_minimale()
{
    $response = $this->postTodo(['texte' => 'ab']); // 2 caractĂšres

    $response->assertSessionHasErrors('texte');
    $this->assertDatabaseCount('todos', 0);
}

ExĂ©cution ciblĂ©e des tests de validation ▶

Pour exécuter uniquement cette classe de tests :

đŸ’» Console
php artisan test --filter=TodosValidationTest

5.3 Cas nominal ➕✔⚓

Il est important de tester aussi le scĂ©nario heureux 😃 :
quand toutes les donnĂ©es sont valides, le Todo doit ĂȘtre créé.

🐘 PHP
<?php
public function test_un_todos_valide_est_cree()
{
    $response = $this->postTodo(); // toutes les valeurs par défaut sont valides

    $response->assertSessionDoesntHaveErrors();
    $response->assertRedirect(); // ou ->assertRedirect('/'); selon ton contrĂŽleur

    $this->assertDatabaseCount('todos', 1);

    $this->assertDatabaseHas('todos', [
        'texte'   => 'Acheter du café',
        'termine' => 0,
        'important' => 0,
    ]);
}

💡 Pourquoi ce test est essentiel ?

  • Il vĂ©rifie que la crĂ©ation fonctionne en cas de donnĂ©es valides.
  • Il sert de rĂ©fĂ©rence pour les autres tests de validation : ils ne doivent empĂȘcher que les donnĂ©es invalides.
  • En cas de modification ultĂ©rieure du contrĂŽleur, ce test permet de dĂ©tecter une rĂ©gression sur le CAS NOMINAL.

Available assertions 📕

Il existe de nombreuses assertions différentes. Elles sont disponible dans la documentation de Laravel.

6. Gestion de la base de donnĂ©es pendant les tests đŸ§°âš“ïžŽ

Jusqu’ici, nous avons Ă©crit des tests qui crĂ©ent des utilisateurs, des listes et des todos Ă  l’aide des factories.
Il est maintenant important de bien comprendre comment Laravel gÚre la base de données pendant les tests.

🎯 Objectifs

  • Comprendre comment isoler chaque test (pas de pollution entre tests).
  • Savoir sur quelle base s’exĂ©cutent les tests (.env.testing).
  • Utiliser les assertions assertDatabaseHas, assertDatabaseMissing, assertDatabaseCount.

6.1 Le trait RefreshDatabase đŸ”„âš“ïžŽ

documentation Laravel sur database-testing

Laravel fournit le trait use Illuminate\Foundation\Testing\RefreshDatabase;

Ce trait, utilisé dans une classe de test, garantit que :

  • les migrations sont exĂ©cutĂ©es pour la base de test,
  • la base est remise Ă  zĂ©ro entre les tests (soit par migrate:fresh, soit par transactions selon le driver).

Exemple (déjà utilisé dans tes tests) :

🐘 PHP
<?php
use Tests\TestCase;
use Illuminate\Foundation\Testing\RefreshDatabase;

class TodosValidationTest extends TestCase
{
    use RefreshDatabase;

    // ...
}
💡 Que fait RefreshDatabase exactement ?
  • Avant le premier test, Laravel exĂ©cute php artisan migrate sur la connexion de test.
  • Entre les tests, il remet la base dans un Ă©tat propre (rollback ou migrate:fresh selon le driver).
  • Cela permet de s’assurer que chaque test est indĂ©pendant des autres : aucun enregistrement laissĂ© par un test ne doit influencer un autre test.

6.2 Quelle base est utilisĂ©e pour les tests ? đŸ—„ïžâš“ïžŽ

Laravel choisit la base de données de test via Le fichier .env.testing ou éventuellement des variables dans phpunit.xml

Dans ton cas, tu as configuré une base MySQL dédiée, par exemple :

dans .env.testing

đŸ’» Console
APP_ENV=testing
APP_DEBUG=true

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=todo_test
DB_USERNAME=laravel_test
DB_PASSWORD=secret

Pendant les tests, on ne touche jamais à la base de développement (todo2025) : tout se passe dans la base todo_test.

💡 Comment vĂ©rifier que Laravel utilise bien la bonne base ?
  • Tu peux temporairement ajouter un test “de debug” :

🐘 PHP
<?php
public function test_voir_la_connexion_de_test() { 
  $this->assertSame('mysql', config('database.default')); 
  $this->assertSame('todo_test', config('database.connections.mysql.database'));
}
- Puis le supprimer une fois que tu es sûre de ta configuration.

6.3 ExĂ©cuter les tests ▶⚓

En gĂ©nĂ©ral, tu n’as pas besoin de te soucier manuellement des migrations : RefreshDatabase s’en charge.

Mais si tu modifies les migrations ou la structure de la base, tu peux forcer un reset complet :

đŸ’» Console
php artisan migrate:fresh --env=testing
php artisan test
💡 Quand utiliser migrate:fresh --env=testing ?
  • Quand tu as modifiĂ© une migration existante (ajout/suppression de colonnes).
  • Quand tu veux repartir d’une base de test complĂštement propre.
  • À Ă©viter en production, bien sĂ»r : cela supprime toutes les tables avant de les recrĂ©er.

7. Tests d’authentification đŸ”âš“ïžŽ

Laravel gĂ©nĂšre automatiquement une sĂ©rie complĂšte de tests d’authentification et de sĂ©curitĂ©, via Breeze ou Jetstream.
Ces tests couvrent déjà toutes les fonctionnalités essentielles :

  • affichage des formulaires de connexion / inscription,
  • validation des identifiants,
  • impossibilitĂ© de se connecter avec un mot de passe incorrect,
  • confirmation de mot de passe,
  • mise Ă  jour du mot de passe,
  • rĂ©initialisation de mot de passe,
  • vĂ©rification d’e-mail,
  • dĂ©connexion de l’utilisateur.

Ces tests se trouvent dans tests/Feature/Auth/

Pourquoi conserver ces tests ? đŸ›Ąïž

Ces tests assurent automatiquement :

  • que l’authentification reste fonctionnelle aprĂšs une mise Ă  jour,
  • que les Ă©tudiants ne cassent pas la sĂ©curitĂ© en modifiant une vue ou une route,
  • que chaque changement sur les routes ou les middlewares continue de respecter les rĂšgles Laravel (auth, verified, etc.).
  • Ils constituent une base solide pour Ă©viter les rĂ©gressions pendant le dĂ©veloppement.

7.2 Tests d’accĂšs aux routes protĂ©gĂ©es (middleware auth) đŸ”’âš“ïžŽ

Il peut ĂȘtre utile d’ajouter un seul test d'exemple dans ton TP pour illustrer comment vĂ©rifier qu’une route est bien protĂ©gĂ©e par auth.

🐘 PHP
<?php
public function test_invite_ne_peut_pas_acceder_aux_todos()
{
    $response = $this->get('/todos');
    $response->assertRedirect(route('login'));
}

7.3 Tests liĂ©s Ă  l'autorisation (Policies) đŸ§­âš“ïžŽ

Lorsque tu ajoutes une policy pour restreindre l’accùs aux Todos :

  • un utilisateur ne peut modifier que ses propres Todos,
  • un utilisateur ne peut supprimer que ses Todos, etc.,

tu peux tester ces rĂšgles simplement avec :

🐘 PHP
<?php
public function test_un_utilisateur_ne_peut_pas_modifier_le_todo_d_un_autre()
{
    $a = User::factory()->create();
    $b = User::factory()->create();
    $todo = Todos::factory()->for($a, 'user')->create();

    $response = $this->actingAs($b)->post("/todos/{$todo->id}/edit", [
        'texte' => 'H4ck',
    ]);

    $response->assertForbidden();
}

Laravel renverra automatiquement 403 Forbidden si la policy refuse l’accùs.

8. IntĂ©gration dans GitHub Actions ⚙⚓

🎯 Objectif : exĂ©cuter automatiquement les tests Laravel Ă  chaque push ou pull request sur GitHub, avec un seuil de couverture minimal.

🔑 IdĂ©e clĂ© :

  • En local, tu peux utiliser MySQL (todo_test).
  • En CI GitHub Actions, on va utiliser SQLite en mĂ©moire (plus simple, plus rapide), en Ă©crivant un .env.testing spĂ©cifique dans le workflow.
  • On active la couverture de tests pour mesurer la part du code rĂ©ellement exĂ©cutĂ©e par les tests, et on impose un seuil minimal (80 %).

8.1. CrĂ©er le workflow tests.yml đŸ§Ÿâš“ïžŽ

Dans ton projet, crée un fichier :

.github/workflows/tests.yml

Avec le contenu suivant :

YAML
name: tests

on:
  push:
  pull_request:

jobs:
  laravel-tests:
    runs-on: ubuntu-latest

    services:
      mysql:
        image: mysql:8.0
        env:
          MYSQL_ROOT_PASSWORD: root
          MYSQL_DATABASE: todo_test
          MYSQL_USER: laravel_test
          MYSQL_PASSWORD: secret
        ports:
          - 3306:3306
        options: >-
          --health-cmd="mysqladmin ping -h 127.0.0.1 -uroot -proot"
          --health-interval=10s
          --health-timeout=5s
          --health-retries=10

    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Setup PHP
        uses: shivammathur/setup-php@v2
        with:
          php-version: '8.3'
          extensions: mbstring, dom, pdo_mysql
          coverage: xdebug

      - name: Install PHP dependencies
        run: composer install --no-interaction --prefer-dist

      # âŹ‡ïž Partie frontend / Vite : crĂ©ation du manifest.json
      - name: Setup Node
        uses: actions/setup-node@v4
        with:
          node-version: '20'

      - name: Install frontend dependencies
        run: npm ci

      - name: Build assets
        run: npm run build

      # âŹ‡ïž On aligne l'environnement de la CI sur ton env de test local
      - name: Prepare environment file
        run: cp .env.testing .env

      - name: Run migrations
        run: php artisan migrate --force

      - name: Run tests with coverage
        env:
          XDEBUG_MODE: coverage
        run: php artisan test --coverage --min=80

8.2. Mesurer la couverture en local đŸ“Šâš“ïžŽ

DĂ©finition 💡

La couverture de code est une métrique qui peut vous permettre de comprendre la part testée de votre source. Cette métrique est trÚs utile, car elle peut vous aider à évaluer la qualité de votre suite de tests.

phpunit regarde quelles lignes ont été "utilisées" dans le code quand on a lancé les tests unitaires. Ensuite en fonction du nombre de lignes utilisées par rapport au nombre de lignes total du fichier, phpunit calcule un taux de couverture pour le fichier.

Dans notre CI :

  • le job GitHub Actions rĂ©ussit si la couverture est ≄ 80 %
  • il Ă©choue si la couverture descend sous 80 %.

En local, tu peux également mesurer la couverture php artisan test --coverage --min=80 --coverage-html ./report

L'option --coverage-html ./report permet de générer un rapport, mesurant la couverture de notre application.

couverture

đŸ˜„ Ici le taux est de \(62.45%\) donc trĂšs largement en dessous du seuil de --coverage --min=80. Donc si on laisse ce taux dans la CI, l'Ă©chec est assurĂ© !

Taux de couverture

▶ Modifier le fichier tests.yml pour vous assurer que le taux de couverture de 80% ne soit pas bloquant.

đŸ’» Console
run: php artisan test --coverage --min=80 || true
Fix problem Xdebug's coverage mode

symptĂŽmes :

đŸ’» Console
(base) PS C:\wamp64\www\todo2026> php artisan test --coverage --min=80
ERROR  Code coverage driver not available. Did you set Xdebug's coverage mode?

Dans le "bon" php.ini, vérifier

đŸ’» Console
xdebug.mode=develop,coverage
xdebug.start_with_request=no

Cela transforme la couverture en garde-fou qualité :
si un développeur commente des tests ou ajoute beaucoup de code non testé, la CI refusera la Pull Request ou signalera le problÚme sur le push.

8.3. Lancer la CI aprĂšs les modifications ✅⚓

Une fois le workflow en place, tu peux tester le pipeline complet :

đŸ’» Console
vendor/bin/pint
php artisan test

git add .
git commit -m "Intégration de l'environnement de tests et de la couverture"
git push -u origin main

Sur GitHub, onglet Actions, tu dois voir le workflow tests s’exĂ©cuter automatiquement Ă  chaque push / pull request.

tests

Fix probleme Vite

Verifier dans vos *.blade.php pour que le SASS soient utiliser (app.blade.php, guest.blade.php)

🐘 PHP
<?php
<!-- Styles et Scripts : Utilisation SASS --> 
@vite(['resources/sass/app.scss', 'resources/js/app.js'])
dans vite.config.php

🐘 PHP
<?php
laravel({
        input: [
            'resources/sass/app.scss',
            'resources/js/app.js',
        ],
        refresh: true,
    }),

Conclusion ✅

  • Un environnement de test Laravel fonctionnel.
  • Des tests unitaires, fonctionnels et API.
  • Une mesure automatique de couverture.
  • Une exĂ©cution automatisĂ©e en CI GitHub Actions.