Cet article explique comment j'ai configuré PHPUnit sur l'environnement de développement local et sur GitHub Actions pour Willow CMS. Au moment de la rédaction de cet article, Willow compte 116 tests et 414 assertions. Cela me donne un niveau de confiance raisonnable pour garantir la fiabilité et la qualité de la base de code. Voici les composants clés de ma configuration de test et la manière dont ils contribuent à ce qui est en passe de devenir une application bien testée.

Une fenêtre de terminal affichant les résultats d'une exécution de test PHPUnit, y compris le temps d'exécution, la configuration, la graine aléatoire et la progression du test.

1. Configuration de PHPUnit (phpunit.xml.dist)

Le fichier phpunit.xml.dist est la configuration principale de PHPUnit. Il définit des paramètres importants tels que les limites de mémoire, les rapports d'erreurs et la structure de la suite de tests. Il est configuré pour inclure lestests/TestCase/ répertoire, où se trouvent tous mes cas de test. Il configure également PHPUnit pour utiliser l'extension de montage CakePHP, qui simplifie la configuration de la base de données pour les tests. Vous l'obtenez gratuitement avec une installation de CakePHP. Une chose que j'ai personnalisée est la randomisation des tests avecexecutionOrder="random" . La randomisation de l'ordre d'exécution des tests est un bon moyen de s'assurer qu'il n'y a pas de dépendances cachées entre les tests. Lorsque les tests sont exécutés dans un ordre fixe, il est possible qu'un test s'appuie par inadvertance sur l'état ou les effets secondaires d'un autre test, et vous ne le sauriez jamais en exécutant les tests dans le même ordre à chaque exécution. Les bons tests sont véritablement indépendants et autonomes.

Une autre personnalisation (mais supprimée depuis dans les commits récents) est l'inclusion d'une classe de nettoyage que j'avais écrite en PHP et qui s'exécutait après l'exécution du test. J'avais des problèmes avec les autorisations entre l'environnement de développement Docker et mon système de fichiers hôte après l'exécution des tests. Il suffit de dire qu'il est possible d'utiliser lephpunit.xml.dist fichier pour configurer le code PHP que vous souhaitez exécuter à différents points d'exécution de PHPUnit.

Vous pouvez jeter un œil à la configuration et au code dont je disposais à l'époque pour faire cela, et en savoir plus sur les extensions PHPUnit dans la documentation PHPUnit . Voici un lien vers le fichier phpunit.xml.dist tel qu'il est aujourd'hui.

2. Intégration continue avec GitHub Actions (.github/workflows/ci.yml)

Pour automatiser mon processus de test, j'ai mis en place un workflow d'intégration continue (CI) à l'aide de GitHub Actions. J'ai trouvé GitHub Actions assez délicat au début, car il fallait effectuer plusieurs cycles de modification de configuration, valider cette modification dans le référentiel, puis la transmettre à GitHub pour voir si cela fonctionnait. Si vous ne connaissez pas les actions GitHub, lisez-en plus ici , c'est génial et je ne fais qu'effleurer la surface.

Le.github/workflows/ci.yml Le fichier définit les étapes à exécuter sur une machine virtuelle dans le cloud chaque fois que j'applique des modifications à des branches spécifiques ou que je crée une demande d'extraction. Les étapes incluent l'installation du logiciel sur la machine ainsi que la configuration. Jetez un œil au fichier pour voir comment je fais des choses comme l'installation et le démarrage de MySQL, l'installation des dépendances PHP avec composer et la copie des fichiers de configuration de CakePHP en place.

Vous n'avez qu'une seule machine virtuelle et un nombre limité de minutes d'exécution par mois. Vous souhaitez donc que cette étape soit aussi légère et rapide que possible. Contrairement à l'environnement de développement Docker (futur article de blog), je ne mets pas en place des outils pratiques comme Redis, PHPMyAdmin, etc. Il s'agit simplement d'une seule machine virtuelle avec les éléments essentiels pour permettre la configuration d'une base de données MySQL, l'installation de dépendances PHP, l'exécution de tests PHPUnit et l'exécution d'analyses statiques avec des outils comme PHP CodeSniffer et PHPStan.

L’objectif final est que mon code soit minutieusement testé et adhère aux normes de codage avant d’être fusionné dans la branche principale.

3. Code de cas de test partagé (tests/TestCase/AppControllerTestCase.php)

Pour garder mon code de test DRY (Don't Repeat Yourself), j'ai créé un fichier réutilisableAppControllerTestCase classe qui étend CakePHPTestCase . Cette classe fournit des fonctionnalités communes pour les tests de contrôleur, qui pour le moment simulent simplement des méthodes d'authentification des utilisateurs via leloginUser méthode. Jetez un oeil au fichier .

Bien que j'aie des tests pour la méthode de connexion , je ne veux pas avoir à effectuer cette connexion « appropriée » (en publiant littéralement les informations d'identification de l'utilisateur de test dans l'action de connexion) chaque fois que je souhaite tester une fonctionnalité dans la zone d'administration. Le code de test doit se concentrer sur l'élément spécifique testé et ne pas introduire de code avant ou après celui que nous essayons de tester et, dans ce cas, cela nécessite un mécanisme pour avoir facilement un point de départ avec un utilisateur connecté.

Jetez un œil à certains tests dans le contrôleur utilisateur qui appellent simplement la méthode loginUser , puis passent directement au test réel.

4. Tests du contrôleur pour les éléments importants (tests/TestCase/Controller/UsersControllerTest.php)

Deux contrôleurs critiques à tester sont lesUsersController et leArticlesController pour le site administrateur et non administrateur de Willow.UsersControllerTest le cours couvre un large éventail de scénarios, notamment la connexion, la déconnexion, l'inscription, la confirmation par e-mail et l'édition des utilisateurs dans la zone d'administration et sur le site frontal pour les utilisateurs enregistrés.

Jetez un œil aux fichiers ici et ici . Une amélioration pour l'avenir serait de les diviser en cas de test distincts pour les contrôleurs administrateurs et non administrateurs. Il est recommandé de tester à la fois les scénarios réussis et non réussis pour s'assurer que l'application se comporte correctement - ne testez pas uniquement le chemin le plus heureux. Par exemple, je vérifie qu'un utilisateur non administrateur et un utilisateur administrateur peuvent se connecter, et je teste également qu'un utilisateur non administrateur ou non connecté se voit refuser l'accès à la zone d'administration.

5. Rapport de couverture du code

Vous devez savoir où vous en êtes pour mieux comprendre où vous devez aller avec les tests. Pour avoir un aperçu de la couverture de mes tests, je génère un rapport de couverture de code à l'aide de PHPUnit. J'ai des alias configurés dans mondev_aliases.txt fichier pour exécuter facilement PHPUnit avec des options de couverture. Jetez un œil ici .

Lephpunit_cov alias génère un rapport de couverture textuel affiché dans le terminal, tandis quephpunit_cov_html produit un rapport HTML dans lewebroot/coverage répertoire. Ces rapports m'aident à identifier les zones de la base de code qui manquent de couverture de test suffisante et me guident dans la rédaction de tests supplémentaires.

Une fenêtre de terminal affichant un rapport de couverture de code pour une application PHP, y compris des détails sur l'exécution, la configuration et les résultats des tests.

Un rapport de couverture de code pour divers fichiers PHP dans une application Web, montrant le pourcentage de lignes, de fonctions et de méthodes, ainsi que de classes et de traits couverts par les tests.

En mettant en œuvre ces pratiques de test, j'ai obtenu plusieurs avantages :

  • Confiance accrue dans la stabilité et la fiabilité de Willow CMS
  • Détection précoce des bugs et des régressions grâce à des tests automatisés - je sais très rapidement si mon dernier code a brisé une fonctionnalité de base
  • Amélioration de la qualité du code en appliquant les normes de codage et les meilleures pratiques
  • Des cycles de développement plus rapides en détectant les problèmes dès le début du processus de développement
  • Collaboration plus facile avec (espérons-le) d'autres développeurs grâce à une stratégie de test bien définie

Pour l’avenir, je prévois d’apporter quelques améliorations :

  • Divisez les cas de test entre les fonctionnalités d'administration et de non-administration pour les contrôleurs clés
  • Élargir la suite de tests pour couvrir davantage de cas extrêmes et de scénarios complexes à mesure que j'ajoute des fonctionnalités
  • Utilisez davantage le conteneur Jenkins Docker local qui fait partie de l'environnement de développement pour exécuter une suite de tests frontaux automatisés via différents navigateurs

Donc, moi-même, je remercie absolument mon prédécesseur pour les efforts qu'il a consacrés à ces tests, et je suis sûr que mon futur moi remerciera moi-même pour avoir continué à les compléter. Les tests ne consistent pas seulement à détecter les bugs ; ils permettent de renforcer la confiance dans votre code et de permettre des cycles de développement plus rapides et plus fiables.

Lectures complémentaires :

Bon test !

Supplémentaire

Maintenant que vous êtes au courant, que diriez-vous de continuer à lire pour une analyse plus détaillée du fichier ci.yml de GitHub Actions.

Plongée en profondeur - ci.yml pour les actions GitHub

Le fichier ci.yml contient toute la magie nécessaire pour automatiser le processus d'intégration continue avec GitHub Actions. Décomposons en détail sa structure et ses fonctionnalités. Vous pouvez jeter un œil à une exécution réelle du flux de travail ici

Nom et événements déclencheurs

Le flux de travail est nomméCI , qui signifie intégration continue. Il est conçu pour s'exécuter automatiquement sur des événements spécifiques :

  • Événements Push : le flux de travail se déclenche lors des push vers lemain ,development ,staging , et toutes les branches qui suivent lefeature/* convention de nommage. Cela garantit que toutes les modifications apportées à ces branches sont automatiquement testées.
  • Événements de demande d'extraction : il se déclenche également sur des demandes d'extraction ciblant lemain ,development , etstaging branches. Cela permet de valider les modifications avant qu'elles ne soient fusionnées dans ces branches critiques.
name: CI

on:
  push:
    branches:
      - main
      - development
      - staging
      - 'feature/*'
  pull_request:
    branches:
      - main
      - development
      - staging

Emploi et environnement

Le flux de travail définit un travail unique nommétest , qui fonctionne sur leubuntu-latest environnement. Cela garantit que les tests sont exécutés dans un environnement Linux cohérent et à jour.

jobs:
  test:
    runs-on: ubuntu-latest

Stratégie matricielle

J'ai choisi une matrice de stratégie pour tester l'application sur plusieurs versions de PHP (8.1 ,8.2 ,8.3 ). Cela signifie simplement que les étapes ci-dessous sont exécutées sur chaque version de PHP et que cela est géré via GitHub Actions pour nous. Nous pourrions ajouter PHP 8.4 au tableau si nous le souhaitions.

strategy:
  matrix:
    php-version: ['8.1', '8.2', '8.3']

Code de paiement :

Leactions/checkout@v4 L'action est utilisée pour cloner le code du référentiel dans l'environnement d'exécution. Il s'agit généralement de la première étape de tout processus CI, car elle fournit la base de code avec laquelle travailler.

steps:
- uses: actions/checkout@v4

Configurer MySQL :

Le service MySQL est démarré et une base de données de testcms_test est créé. Notez que nous ne chargeons aucun SQL pour créer des tables de base de données - cela est géré par CakePHP exécutant les migrations lorsque nous exécutons les tests car le fichier phpunit.xml.dist spécifie le fichier d'amorçage spécifique au test pour lancer les choses avec la lignebootstrap="tests/bootstrap.php"

- name: Setup MySQL
  run: |
    sudo service mysql start
    mysql -e 'CREATE DATABASE IF NOT EXISTS cms_test;' -uroot -proot
    mysql -e 'SHOW DATABASES;' -uroot -proot

Configurer PHP :

Leshivammathur/setup-php@v2 L'action est utilisée pour configurer les versions PHP spécifiées à partir de la matrice une par une. Elle installe également les extensions PHP nécessaires commembstring ,intl ,pdo_mysql , etc., et permet une couverture de code avecxdebug .

- name: Setup PHP
  uses: shivammathur/setup-php@v2
  with:
    php-version: ${{ matrix.php-version }}
    extensions: mbstring, intl, pdo_mysql, pcntl, sockets, bcmath, zip
    coverage: xdebug

Installer les dépendances de Composer :

Composer est configuré pour utiliser la version PHP de la matrice et les dépendances sont installées .--no-interaction ,--prefer-dist , et--ignore-platform-reqs Les drapeaux garantissent un processus d'installation fluide et non interactif. C'est l'étape qui installe le framework CakePHP et d'autres éléments utilisés par Willow CMS dans le dossier /vendors.

- name: Install Composer dependencies
  run: |
    composer config platform.php ${{ matrix.php-version }}
    composer update --no-interaction --prefer-dist --ignore-platform-reqs

Copier les configurations :

Les fichiers de configuration sont copiés d'un répertoire spécifié vers le répertoire de configuration de l'application. Cette étape garantit que l'application utilise les configurations spécifiques à l'environnement correctes pendant les tests. Étant donné que l'environnement de développement Docker comporte plusieurs conteneurs pour différents services et que GitHub Actions attribue au travail une seule machine virtuelle, il existe des différences de configuration importantes telles que l'adresse du serveur MySQL. Nous pourrions faire plus avec les variables d'environnement pour cela, mais pour l'instant, c'est simple et ça fonctionne.

- name: Copy Configs
  run: |
    cp docker/github/cms_app_local_github.php config/app_local.php
    cp docker/github/app_github.php config/app.php

Déboguer app_local.php :

Le contenu deapp_local.php sont affichés. Ceci est utile pour déboguer et vérifier que la configuration correcte est utilisée en cas de problème.

- name: Debug app_local.php
  run: cat "config/app_local.php"

Exécutez PHPUnit :

Les tests PHPUnit sont exécutés avec l'affichage des erreurs activé.XDEBUG_MODE: coverage La variable d'environnement est définie pour collecter les données de couverture du code afin que vous puissiez lire les statistiques de couverture dans la sortie. Je pense qu'à l'avenir, je pourrais désactiver cette option car dans la pratique, elles ne sont pas très faciles à lire sur GitHub.

- name: Run PHPUnit
  run: php -d display_errors=on -d error_reporting=E_ALL vendor/bin/phpunit
  env:
    XDEBUG_MODE: coverage

Exécutez PHP CodeSniffer :

PHP CodeSniffer est exécuté pour garantir que le code adhère aux normes de codage CakePHP. Cela permet de maintenir la qualité et la cohérence du code. Il détectera les erreurs d'espaces blancs, les comparaisons Yoda, les instructions d'utilisation PHP qui ne sont pas dans l'ordre alphabétique, les variables inutilisées et plus encore. Notez que je n'exécute ceci que sur les dossiers src et tests car ils contiennent le code PHP pur et les dossiers comme les modèles sont un mélange de PHP et HTML.

- name: Run PHP CodeSniffer
  run: vendor/bin/phpcs --standard=vendor/cakephp/cakephp-codesniffer/CakePHP src/ tests/

Exécutez PHPStan :

PHPStan est utilisé pour l'analyse statique du code. Il vérifie les erreurs potentielles et les problèmes de qualité du code.continue-on-error: true L'option permet au flux de travail de continuer même si PHPStan détecte des problèmes, ce qui peut être utile pour les retours non bloquants. Vous verrez que j'ai configuré le travail pour continuer en cas d'erreur avec cette étape car pour l'instant, se concentrer sur l'écriture de tests et adhérer aux normes de codage CakePHP est suffisant.

- name: Run PHPStan
  run: php -d memory_limit=-1 vendor/bin/phpstan analyse src/
  continue-on-error: true

Mots clés

Unité PHP Couverture du code Contrôleurs CI GâteauPHP Actions GitHub CodeQualité Essai