Les tests unitaires sont essentiels dans le développement de logiciels, car ils permettent de s’assurer que les composants de votre application fonctionnent comme prévu de manière isolée. En écrivant des tests pour des unités de code spécifiques, vous pouvez identifier et corriger les erreurs dès le début du développement, ce qui permet d’obtenir des logiciels plus fiables et plus stables.

Dans un pipeline d’intégration continue/de livraison continue (CI/CD), vous pouvez exécuter ces tests automatiquement après avoir apporté des modifications à la base de code. Cela permet de s’assurer que le nouveau code n’introduit pas d’erreurs ou ne casse pas les fonctionnalités existantes.

Cet article souligne l’importance des tests unitaires dans les applications Lavarel, en détaillant comment écrire des tests unitaires pour une application Laravel déployée à l’aide du service d’hébergement d’applications de Kinsta.

Introduction à PHPUnit

PHPUnit est un framework de test largement utilisé au sein de l’écosystème PHP, conçu pour les tests unitaires. Il dispose d’une suite robuste d’outils pour créer et exécuter des tests, ce qui en fait une ressource essentielle pour assurer la fiabilité et la qualité de votre base de code.

Laravel prend en charge les tests avec PHPUnit et est livré avec des méthodes d’aide pratiques qui vous permettent de tester votre application.

La mise en place de PHPUnit dans un projet Laravel implique une configuration minimale. Laravel fournit un environnement de test pré-configuré, comprenant un fichier phpunit.xml et un répertoire de tests dédié pour tes fichiers de test.

Vous pouvez aussi modifier le fichier phpunit.xml pour définir des options personnalisées afin d’obtenir une expérience de test sur mesure. Vous pouvez également créer un fichier d’environnement .env.testing dans le dossier racine du projet au lieu d’utiliser le fichier .env.

Disposition des tests par défaut dans Laravel

Laravel fournit une disposition structurée des répertoires par défaut. Le répertoire racine de votre projet Laravel contient un répertoire tests avec les sous-répertoires Feature et Unit . Cette disposition permet de séparer facilement les différents types de tests et de maintenir un environnement de test propre et organisé.

Le fichier phpunit.xml d’un projet Laravel est crucial pour orchestrer le processus de test, assurer la cohérence des exécutions de tests et vous permettre de personnaliser le comportement de PHPUnit en fonction des exigences du projet. Il vous permet de définir comment exécuter les tests, notamment en définissant les suites de tests, en spécifiant l’environnement de test et en configurant les connexions aux bases de données.

Ce fichier spécifie également que la session, le cache et l’e-mail doivent être définis sur le pilote de tableau, ce qui garantit qu’aucune donnée de session, de cache ou d’e-mail ne persiste lors de l’exécution des tests.

Vous pouvez effectuer plusieurs types de tests sur votre application Laravel :

  • Tests unitaires – se concentrent sur les composants individuels de votre code, tels que les classes, les méthodes et les fonctions. Ces tests restent isolés de l’application Laravel et vérifient que des unités de code spécifiques fonctionnent comme prévu. Notez que les tests définis dans le répertoire tests/Unit ne démarrent pas l’application Laravel, ce qui signifie qu’ils ne peuvent pas accéder à la base de données ou aux autres services offerts par le framework.
  • Tests de fonctionnalités – valident les fonctionnalités plus générales de votre application. Ces tests simulent des requêtes et des réponses HTTP, vous permettant de tester les routes, les contrôleurs et l’intégration de divers composants. Les tests de fonctionnalités permettent de s’assurer que les différentes parties de votre application fonctionnent ensemble comme prévu.
  • Tests de navigateur – vont plus loin en automatisant les interactions avec le navigateur. Les tests utilisent Laravel Dusk, un outil d’automatisation et de test du navigateur, pour simuler les interactions des utilisateurs, comme remplir des formulaires et cliquer sur des boutons. Les tests de navigateur sont cruciaux pour valider le comportement de votre application et l’expérience utilisateur dans les navigateurs du monde réel.

Concepts de développement piloté par les tests

Le développement piloté par les tests (TDD) est une approche de développement de logiciels qui met l’accent sur les tests avant d’implémenter le code. Cette approche suit un processus connu sous le nom de cycle « red-green-refractor ».

Le cycle de développement piloté par les tests montrant le cycle red-green-refractor.
Le cycle de développement piloté par les tests montrant le cycle red-green-refractor.

Voici une explication de ce cycle :

  • Phase rouge – Écrivez un nouveau test pour définir une fonctionnalité ou une amélioration d’un test existant avant d’implémenter le code réel. Le test doit échouer (comme le signifie le « rouge ») parce qu’il n’y a pas de code correspondant pour le faire passer.
  • Phase verte – Écrivez juste assez de code pour faire passer le test qui a échoué, en le faisant passer du rouge au vert. Le code ne sera pas optimal, mais il répond aux exigences du cas de test correspondant.
  • Phase de remaniement – Remaniez le code pour améliorer sa lisibilité, sa maintenabilité et ses performances sans modifier son comportement. À ce stade, vous pouvez confortablement apporter des modifications au code sans vous inquiéter des problèmes de régression, car les cas de test existants les détectent.

Le TDD présente plusieurs avantages :

  • Détection précoce des bogues – Le TDD permet de détecter les bogues dès le début du processus de développement, ce qui contribue à réduire le cout et le temps de correction des problèmes plus tard dans le cycle de développement.
  • Amélioration de la conception – Le TDD encourage un code modulaire et faiblement couplé pour une meilleure conception du logiciel. Il vous encourage à réfléchir à l’interface et aux interactions des composants avant la mise en œuvre.
  • Confiance dans le remaniement – Vous pouvez remanier le code en toute confiance, sachant que les tests existants identifient rapidement les régressions introduites lors du remaniement.
  • Documentation vivante – Les cas de test servent de documentation vivante en fournissant des exemples de la façon dont le code doit se comporter. Cette documentation est toujours à jour puisque les tests qui échouent indiquent des problèmes dans le code.

Dans le développement Laravel, vous appliquez les principes TDD en écrivant des tests pour les composants tels que les contrôleurs, les modèles et les services avant de les mettre en œuvre.

L’environnement de test de Laravel, notamment PHPUnit, fournit des méthodes et des assertions pratiques pour faciliter le TDD, ce qui garantit que vous pouvez créer des tests significatifs et suivre efficacement le cycle red-green-refactor.

Exemples de base de tests unitaires

Cette section explique comment écrire un test simple pour vérifier les fonctionnalités de ton modèle.

Pré-requis

Pour suivre le cours, vous avez besoin de ce qui suit :

  • Répondre aux pré-requis listés dans le guide du blog Laravel.
  • Une application Laravel. Ce tutoriel utilise l’application créée dans le guide dont le lien figure ci-dessus. Vous pouvez le lire et créer l’application du blog, mais si vous n’avez besoin que du code source pour mettre en œuvre les tests, suivez les étapes ci-dessous.
  • Xdebug installé et configuré avec le mode couverture activé.

Configurer le projet

  1. Exécutez cette commande dans une fenêtre de terminal pour cloner le projet.
    git clone https://github.com/VirtuaCreative/kinsta-laravel-blog.git
  2. Déplacez-vous dans le dossier du projet et exécutez la commande composer install pour installer les dépendances du projet.
  3. Renommez le fichier env.example en .env.
  4. Exécutez la commande php artisan key:generate pour générer une clé d’application.

Créer et exécuter des tests

Pour commencer, assurez-vous d’avoir le code du projet sur votre machine. Le modèle que vous aller tester est le modèle Post défini dans le fichier app/Http/Models/Post.php. Ce modèle comprend plusieurs attributs à remplir, tels que title, description, et image.

Votre tâche consiste à élaborer des tests unitaires simples pour ce modèle. L’un d’eux vérifie que les attributs sont correctement définis, tandis que l’autre examine l’affectation de masse en essayant d’affecter un attribut non remplissable.

  1. Exécutez la commande php artisan make:test PostModelFunctionalityTest --unit pour créer un nouveau scénario de test. L’option --unit spécifie qu’il s’agit d’un test unitaire et l’enregistre dans le répertoire tests/Unit .
  2. Ouvrez le fichier tests/Unit/PostModelFunctionalityTest.php et remplacez la fonction test_example par ce code :
    public function test_attributes_are_set_correctly()
       {
           // create a new post instance with attributes
           $post = new Post([
               'title' => 'Sample Post Title',
               'description' => 'Sample Post Description',
               'image' => 'sample_image.jpg',
           ]);
    
           // check if you set the attributes correctly
           $this->assertEquals('Sample Post Title', $post->title);
           $this->assertEquals('Sample Post Description', $post->description);
           $this->assertEquals('sample_image.jpg', $post->image);
       }
    
       public function test_non_fillable_attributes_are_not_set()
       {
           // Attempt to create a post with additional attributes (non-fillable)
           $post = new Post([
               'title' => 'Sample Post Title',
               'description' => 'Sample Post Description',
               'image' => 'sample_image.jpg',
               'author' => 'John Doe',
           ]);
    
           // check that the non-fillable attribute is not set on the post instance
           $this->assertArrayNotHasKey('author', $post->getAttributes());
       }

    Ce code définit deux méthodes de test.

    La première crée une instance Post avec les attributs spécifiés et, à l’aide de la méthode d’assertion assertEquals, vérifie que les attributs title, description et image sont correctement définis.

    La seconde méthode tente de créer une instance Post avec un attribut supplémentaire non remplissable (author) et vérifie que cet attribut n’est pas défini sur l’instance de modèle à l’aide de la méthode d’assertion assertArrayNotHasKey.

  3. Assurez-vous d’ajouter l’instruction use suivante dans le même fichier :
    use App\Models\Post;
  4. Exécutez la commande php artisan config:clear pour vider le cache de configuration.
  5. Pour effectuer ces tests, exécutez la commande suivante :
    php artisan test tests/Unit/PostModelFunctionalityTest.php

    Tous les tests doivent réussir, et le terminal doit afficher les résultats et le temps total d’exécution des tests.

Déboguer les tests

Si les tests échouent, vous pouvez les déboguer en suivant les étapes suivantes :

  1. Examinez le message d’erreur dans le terminal. Laravel fournit des messages d’erreur détaillés qui mettent le doigt sur le problème. Lisez attentivement le message d’erreur pour comprendre pourquoi le test a échoué.
  2. Inspectez les tests et le code que vous êtes en train de tester pour identifier les anomalies.
  3. Assurez-vous de configurer correctement les données et les dépendances nécessaires au test.
  4. Utilisez des outils de débogage comme la fonction dd() de Laravel pour inspecter les variables et les données à des points spécifiques de votre code de test.
  5. Une fois que vous avez identifié le problème, apportez les modifications nécessaires et exécutez à nouveau les tests jusqu’à ce qu’ils réussissent.

Tests et bases de données

Laravel offre un moyen pratique de configurer un environnement de test en utilisant une base de données SQLite en mémoire, qui est rapide et ne persiste pas les données entre les exécutions de tests. Pour configurer l’environnement de la base de données de test et écrire des tests qui interagissent avec la base de données, suivez les étapes ci-dessous :

  1. Ouvrez le fichier phpunit.xml et décommentez les lignes de code suivantes :
    <env name="DB_CONNECTION" value="sqlite"/>
    <env name="DB_DATABASE" value=":memory:"/>
  2. Exécutez la commande php artisan make:test PostCreationTest --unit pour créer un nouveau cas de test.
  3. Ouvrez le fichier tests/Unit/PostCreationTest.php et remplace la méthode test_example par le code ci-dessous :
    public function testPostCreation()
       {
           // Create a new post and save it to the database
           $post = Post::create([
               'title' => 'Sample Post Title',
               'description' => 'Sample Post Description',
               'image' => 'sample_image.jpg',
           ]);
    
           // Retrieve the post from the database and assert its existence
           $createdPost = Post::find($post->id);
           $this->assertNotNull($createdPost);
           $this->assertEquals('Sample Post Title', $createdPost->title);
       }
  4. Assurez-vous d’ajouter l’instruction use suivante :
    use App\Models\Post;

    Actuellement, la classe PostCreationTest étend la classe de base PHPUnitFrameworkTestCase. La classe de base est couramment utilisée pour les tests unitaires lorsqu’on travaille avec PHPUnit directement, en dehors de Laravel, ou lorsqu’on écrit des tests pour un composant qui n’est pas étroitement couplé à Laravel. Cependant, vous avez besoin d’accéder à la base de données, ce qui signifie que vous devez modifier la classe PostCreationTest pour qu’elle étende la classe TestsTestCase.

    Cette dernière classe adapte la classe PHPUnitFrameworkTestCase aux applications Laravel. Elle fournit des fonctionnalités supplémentaires et une configuration spécifique à Laravel, comme l’alimentation de la base de données et la configuration de l’environnement de test.

  5. Assurez-vous de remplacer la déclaration use PHPUnitFrameworkTestCase; par use TestsTestCase;.N’oubliez pas que vous avez configuré l’environnement de test pour qu’il utilise une base de données SQLite en mémoire. Vous devez donc migrer la base de données avant d’exécuter les tests. Pour cela, utilise le trait IlluminateFoundationTestingRefreshDatabase. Ce trait migre la base de données si le schéma n’est pas à jour et réinitialise la base de données après chaque test pour s’assurer que les données du test précédent n’interfèrent pas avec les tests suivants.
  6. Ajoutez l’instruction use suivante au fichier tests/Unit/PostCreationTest.php pour incorporer ce trait dans votre code :
    use Illuminate\Foundation\Testing\RefreshDatabase;
  7. Ensuite, ajoutez la ligne de code suivante juste avant la méthode testPostCreation:
    use RefreshDatabase;
  8. Exécutez la commande php artisan config:clear pour vider le cache de configuration.
  9. Pour effectuer ce test, exécutez la commande suivante :
    php artisan test tests/Unit/PostCreationTest.php

    Les tests doivent réussir, et le terminal doit afficher les résultats du test et la durée totale du test.

Tests de fonctionnalités

Alors que les tests unitaires vérifient les composants individuels de l’application de manière isolée, les tests de fonctionnalités vérifient des parties plus importantes du code, comme la manière dont plusieurs objets interagissent. Les tests de fonctionnalités sont essentiels pour plusieurs raisons :

  1. Validation de bout en bout – Confirme que l’ensemble de la fonctionnalité fonctionne de manière transparente, y compris les interactions entre les différents composants tels que les contrôleurs, les modèles, les vues et même la base de données.
  2. Test de bout en bout – Couvre l’ensemble du flux d’utilisateurs, de la requête initiale à la réponse finale, ce qui peut permettre de découvrir des problèmes que les tests unitaires risquent de manquer. Cette capacité les rend précieux pour tester les parcours des utilisateurs et les scénarios complexes.
  3. Assurance de l’expérience utilisateur – imite les interactions des utilisateurs, ce qui permet de vérifier que l’expérience utilisateur est cohérente et que la fonctionnalité fonctionne comme prévu.
  4. Détection des régressions – Permet de détecter les régressions et les changements qui brisent le code lors de l’introduction d’un nouveau code. Si une fonctionnalité existante commence à échouer lors d’un test de fonctionnalité, cela indique que quelque chose s’est cassé.

Maintenant, créez un test de fonctionnalité pour PostController dans le fichier app/Http/Controllers/PostController.php. Vous vous concentrez sur la méthode store, qui valide les données entrantes et crée et stocke les messages dans la base de données.

Le test simule un utilisateur qui crée un nouvel article par le biais d’une interface web, en s’assurant que le code stocke l’article dans la base de données et redirige l’utilisateur vers la page Index des articles après la création. Pour cela, suivez les étapes suivantes :

  1. Exécutez la commande php artisan make:test PostControllerTest pour créer un nouveau cas de test dans le répertoire tests/Features.
  2. Ouvrez le fichier tests/Feature/PostControllerTest.php et remplacez la méthode test_example par ce code :
    use RefreshDatabase; // Refresh the database after each test
    
       public function test_create_post()
       {
           // Simulate a user creating a new post through the web interface
           $response = $this->post(route('posts.store'), [
               'title' => 'New Post Title',
               'description' => 'New Post Description',
               'image' => $this->create_test_image(),
           ]);
    
           // Assert that the post is successfully stored in the database
           $this->assertCount(1, Post::all());
    
           // Assert that the user is redirected to the Posts Index page after post creation
           $response->assertRedirect(route('posts.index'));
       }
    
       // Helper function to create a test image for the post
       private function create_test_image()
       {
           // Create a mock image file using Laravel's UploadedFile class
           $file = UploadedFile::fake()->image('test_image.jpg');
    
           // Return the path to the temporary image file
           return $file;
       }

    La fonction test_create_post simule un utilisateur qui crée un nouvel article en effectuant une requête POST vers la route posts.store avec des attributs spécifiques, notamment une image fictive générée à l’aide de la classe UploadedFile de Laravel.

    Le test affirme ensuite que le code a réussi à stocker l’article dans la base de données en vérifiant le compte de Post::all(). Il vérifie que le code redirige l’utilisateur vers la page Index des articles après la création de l’article.

    Ce test garantit que la fonctionnalité de création d’articles fonctionne et que l’application gère correctement les interactions avec la base de données et les redirections après la post-soumission.

  3. Ajoutez les instructions use suivantes au même fichier :
    use App\Models\Post;
    use Illuminate\Http\UploadedFile;
  4. Exécutez la commande php artisan config:clear pour vider le cache de configuration.
  5. Pour effectuer ce test, exécutez cette commande :
    php artisan test tests/Feature/PostControllerTest.php

    Le test doit réussir, et le terminal doit afficher les résultats du test et le temps total d’exécution du test.

Confirmer la couverture du test

La couverture des tests fait référence à la part de la base de code que tes tests d’unité, de fonctionnalité ou de navigateur vérifient, exprimée en pourcentage. Elle vous aide à identifier les zones non testées de ta base de code et les zones sous testées contenant potentiellement des bogues.

Des outils comme la fonction de couverture du code de PHPUnit et le rapport de couverture intégré de Laravel génèrent des rapports indiquant les parties de votre base de code que vos tests couvrent. Ce processus fournit des informations essentielles sur la qualité de tes tests et vous aide à vous concentrer sur les zones qui pourraient nécessiter des tests supplémentaires.

Générer un rapport

  1. Supprimez les fichiers tests/Feature/ExampleTest.php et tests/Unit/ExampleTest.php, car vous ne les avez pas modifiés et ils pourraient provoquer des erreurs.
  2. Exécutez la commande php artisan test --coverage dans une fenêtre de terminal. Vous devriez recevoir une sortie comme la suivante :
    Exécution de la commande php artisan test --coverage
    Exécution de la commande php artisan test –coverage

    Le rapport de couverture de code montre les résultats des tests, le nombre total de tests réussis et le temps d’exécution des résultats. Il répertorie également chaque composant de votre base de code et son pourcentage de couverture du code. Les pourcentages représentent la proportion du code que vos tests couvrent.

    Par exemple, Models/Post a une couverture de 100 %, ce qui signifie que toutes les méthodes et lignes de code du modèle sont couvertes. Le rapport sur la couverture du code affiche également la couverture totale – la couverture globale du code pour l’ensemble de la base de code. Dans ce cas, les tests ne couvrent que 65,3 % du code.

  3. Pour spécifier un seuil minimum de couverture, exécutez la commande php artisan test --coverage --min=85. Cette commande définit un seuil minimum de 85 %. Vous devriez recevoir la sortie suivante :
    Test avec un seuil minimum de 85 %.
    Test avec un seuil minimum de 85 %.

    Les suites de tests échouent parce que le code n’atteint pas le seuil minimum fixé de 85 %.

    Bien que l’objectif soit d’atteindre une couverture de code plus élevée – souvent 100 % – il est plus important de tester minutieusement les parties critiques et complexes de votre application.

Résumé

En adoptant les meilleures pratiques décrites dans cet article, telles que la rédaction de tests significatifs et complets, le respect du cycle red-green-refactor en TDD et l’exploitation des fonctionnalités de test fournies par Laravel et PHPUnit, vous pouvez créer des applications robustes et de haute qualité.

De plus, vous avez la possibilité d’héberger votre application Laravel dans l’infrastructure rapide, sécurisée et fiable de Kinsta. En outre, vous pouvez utiliser l’API de Kinsta pour lancer des déploiements au sein de vos pipelines CI/CD par le biais de plateformes telles que GitHub Actions, CircleCI, et plus encore.

Jeremy Holcombe Kinsta

Rédacteur en chef du contenu et du marketing chez Kinsta, développeur web WordPress et rédacteur de contenu. En dehors de WordPress, j'aime la plage, le golf et le cinéma. J'ai aussi des problèmes avec les personnes de grande taille ;).