Avoir des tests PHPUnit projet avec intégration Gitlab CI

Créé le 23/11/2020

Dernière mise à jour le 23/11/2020

Étiquettes

Au niveau de la configuration du projet, nous nous plaçons dans le cas d’une configuration du site exportée globalement, opposée à une configuration placée dans des modules.

Il existe de nombreux articles et pages de documentation sur PHPUnit avec Drupal, cet article va se concentrer sur l’aspect utilisation dans un projet, même si un petit rappel (très simplifié) sera fait pour pouvoir poser la problématique et comment y remédier.

PHPUnit dans Drupal

Le framework de tests PHPUnit a été ajouté au noyau Drupal 8 depuis les débuts de son développement (voir le change records de 2013). Auparavant il y avait un outil propre à Drupal “Simpletest”, la PHPUnit initiative a permis de convertir les tests Simpletest du noyau en tests PHPUnit.

Actuellement on trouve 4 types de tests PHPUnit :

  • Unit : Pour tester les méthodes d’une classe individuellement.
    • Pas de base de données.
    • Besoin de “mocker” tout ce qui va être utilisé par le code (services, résultat des appels de méthodes, etc.)
  • Kernel : Pour tester des interactions entre classes.
    • Connexion à une base de données possible, mais besoin de lister explicitement les tables qui seront installées.
    • Possibilité d’activer des modules, mais besoin de les lister individuellement (pas de gestion de dépendance récursive).
  • Functional : Pour tester un site complet.
    • Connexion à une base de données.
    • Site installé via un profil.
    • Possibilité d’activer des modules (et les dépendances sont gérées).
  • Functional Javascript : Idem Functional mais avec un émulateur de navigateur afin de pouvoir tester l’exécution du Javascript.

Problématique

La difficulté d’implémentation réside dans le “setup”, la mise en place des pré-requis pour que le test soit exécuté. Nous allons rester ici sur des tests où le site est autosuffisant, sans dépendance externe comme Redis, SolR, un autre site Drupal (voir le module Entity Share ;) ), etc.

Pour les tests de type Unit, il faut tout “mocker” et lorsque le code va changer, même très peu, en général ces tests vont devoir être ajustés.

Pour les tests de types Kernel, Functional et Functional Javascript, il faut activer les modules nécessaires, puis il faut mettre en place la configuration nécessaires, puis créer les contenus, etc. Avant de pouvoir tester.

Exemple : un test qui vérifie si une vue affiche les bons résultats est simple, il suffit d’aller sur l’URL de la vue et de tester si les résultats affichés sont les bons. Le travail est dans la configuration, la création des contenus, etc.

Autant dans le noyau Drupal et les modules communautaires, le setup de certains tests est souvent fastidieux, mais heureusement il y a des cas où cela est simple, autant une fois que c’est en place, en général, cela ne va plus trop bouger et les tests assurent la stabilité du module.

Seulement sur un projet où la configuration est exportée globalement et change au cours de la vie du projet, le but est d’éviter dans le setup des tests, a minima, la partie configuration.

En s’appuyant sur la configuration exportée pour tester son fonctionnement et ne pas tester une configuration recréée dans les tests pour les tests, on évite le problème de devoir maintenir la configuration à plusieurs endroits.

Hors le problème est que dans les tests automatisés, il n’est pas possible d’importer la configuration exportée sur un projet car le test est isolé de celle-ci.

Nous allons voir comment remédier à cela.

Solution

Comme il est possible dans les tests d’activer des modules et que la configuration de ces modules soit importée, l’idée est de générer automatiquement un module contenant la configuration du site avant le lancement des tests et d’importer la configuration via ce module.

Ce qui assure que les tests s’appuient sur la configuration du site et évite de devoir maintenir la configuration à plusieurs endroits dans le projet.

Cette astuce m’a été présentée par Benjamin Rambaud sur son dépôt https://gitlab.com/beram-drupal/drupal-ci.

J’ai ainsi mis en place cette logique sur mon template projet, et ai créé une branche dédiée avec une configuration et des tests d’exemples. J’ai poussé plus loin les tests d’exemples avec des tests de types Functional et Functional Javascript qui ont entraîné des difficultés avec Gitlab CI.

Mise en place et problèmes rencontrés

Voici les étapes pour inclure ce système de tests :

  1. Reprise et mise à jour du script de création du module de configuration : https://gitlab.com/florenttorregrosa-drupal/docker-drupal-project/-/blob/8.x/scripts/tests/manage-tests-module.sh
  2. Adaptation du fichier de configuration PHPUnit : https://gitlab.com/florenttorregrosa-drupal/docker-drupal-project/-/blob/8.x/scripts/tests/phpunit.xml.dist
    1. j’ai testé la base de données Sqlite en mémoire pour les tests, ce qui a fonctionné. Mais je suis finalement revenu sur la même base de données que celle utilisée dans Docker Compose car autant être identique avec la base de données du projet.
    2. Là où avant je suivais le fichier PHPUnit du noyau Drupal, désormais uniquement le code custom est listé. D’ailleurs, en cas de modules avec des sous-modules qui ont leurs propres tests, il faut lister explicitement les sous-modules. Dommage que le wildcard ** ne fonctionne pas.
  3. Adaptation du script pour lancer la commande PHPUnit : https://gitlab.com/florenttorregrosa-drupal/docker-drupal-project/-/blob/8.x/scripts/tests/run-tests-phpunit.sh
  4. Dans mes scripts, j’essaie de faire en sorte que les chemins soient relatifs ou gérés par des variables. Or la commande phpunit n’arrive pas à prendre en compte un chemin relatif pour indiquer l’emplacement du fichier de configuration PHPUnit. Obligé de faire en chemin absolu. Et du coup pour Gitlab CI, heureusement que Gitlab CI fournit la variable “CI_PROJECT_DIR”, j’ai donc fait une légère adaptation des chemins dans les scripts Shell pour la prendre en compte si besoin au début du fichier https://gitlab.com/florenttorregrosa-drupal/docker-drupal-project/-/blob/8.x/scripts/script-parameters.sh

Avec ces étapes il est possible d’avoir des tests PHPUnit de type Unit et Kernel. Cela correspond à ce commit.

Même si sur mon environnement de développement j’avais ce qu’il fallait pour les tests Functional et Functional Javascript, il allait falloir adapter la configuration Gitlab CI pour que cela fonctionne. En effet, ces types de tests ont besoin d’un serveur web en fonctionnement et d’un émulateur de navigateur.

D’où ce commit et les ajustements :

  1. Le fichier .ht.router.php sert à une utilisation du serveur web embarqué dans PHP. Je ne m’en étais jamais servi auparavant, mais pour rester simple et éviter d’avoir une image Docker à faire et maintenir en plus ou de modifier une existante, je reconnais que c’est pratique. D’où le fait de ne plus l’exclure du plugin Drupal scaffold.
  2. La difficulté suivante a été au niveau réseau Docker dans Gitlab CI car le serveur web est dans le conteneur PHP avec les sources, donc l’URL du site est 127.0.0.1, ce qui fonctionne pour les tests Functional mais pas les Functional Javascript car l’émulateur de navigateur est dans un autre conteneur (chromedriver) donc pour ce conteneur, à l’adresse 127.0.0.1 il n’y a pas de site. En regardant comment Drupal Spoons fonctionnait, j’ai vu qu’il y avait l’utilisation d’un alias “build”, et c’est uniquement au détour d’une phrase non mise en avant dans la documentation de Gitlab CI qu’il est expliqué que automatiquement le conteneur utilisé pour la tâche est accessible via l’alias “build”. Et chose bien faite dans la configuration PHPUnit, les variables d’environnements dans le fichier sont surchargeables, il est ainsi simple d’avoir le fichier de configuration prêt pour l’environnement de développement et surchargé via variables d’environnement dans Gitlab CI.
  3. Il ne restait plus que des problèmes de permission, de mauvais chemins (à cause de Drupal paranoia pour certains).

Désormais, les 4 types de tests PHPUnit sont exécutés sur Gitlab CI.

Prochaines étapes

Vu que c’est un apport récent dans le template projet, il y aura sans doute des améliorations et ajustements faits. Voici pourquoi je mets des liens vers des fichiers et des commits et non du code dans cet article.

Un aspect que je ne gère pas encore avec Gitlab CI est la génération d’artéfact, ce qui fait que les fichiers html de sortie des tests ne sont pas accessibles, ce qui peut être contraignant en cas de débug si les tests passent en local et pas sur Gitlab CI par exemple.

En 2017, J’avais initialisé une configuration de tests Behat, il faudra que je la re-teste si elle est toujours valable et que j’essaie de faire en sorte que ce soit exécutable dans Gitlab CI. S’en suivra les tests d'accessibilité Pa11y.

Contrairement à PHPUnit qui crée un environnement entier et le détruit une fois les tests terminés, Behat et Pa11y sont des outils qui s’exécutent sur un environnement existant.

Remerciements

Merci à Benjamin Rambaud de m’avoir montré cette astuce.

Merci également à Fabien Clément pour m’avoir montré une astuce permettant aux tests d’importer la configuration exportée d’un projet. Astuce que j’ai préféré ne pas utiliser car plus complexe en termes de raisonnement je trouve.

Merci à Smile de m’avoir permis de passer du temps sur ce sujet.

Ressources

Ajouter un commentaire