Dans cet article je vous propose un retour d’expérience quant à l’utilisation du framework de test Codeception, que j’ai eu l’occasion de mettre en place pour de nos clients dans le cadre d’une mission au sein de son équipe.
Contexte
Lors de mon intégration au sein cette équipe de développement (PHP uniquement), un des défis fut de migrer un site en Symfony 2 en Symfony 3 pour des raisons évidentes de sécurité et de maintenance. Mais cette montée de version fut également le moment idéal pour refondre une partie de la logique métier ainsi que le système de paiement de cette plateforme.
Une refonte est souvent une occasion parfaire pour améliorer la qualité et la robustesse du code et mettre en place des tests unitaires et fonctionnels. C’est ce que nous avons décidé de faire …
Présentation
Codeception fût le framework idéal correspondant parfaitement à notre besoin. Au-delà du fait que ce soit du PHP, il permet de faire des tests d’acceptances, fonctionnels et unitaires et donc de couvrir tous les aspects d’une application ou d’un site web.
Les tests d’acceptances
En temps normal, ils peuvent être effectués par une personne non technique. Cette personne peut être un testeur, un responsable ou même un client, et n’a besoin que d’un navigateur Web pour vérifier le bon fonctionnement de votre site.
Codeception permet, de manière simple, de reproduire ces actions à travers des scénarios et de les exécuter automatiquement.
Deux solutions sont proposées par le framework pour mettre en place ces tests d’acceptances :
- PHP Browser
Solution simple et rapide car il n’est pas nécessaire d’exécuter un navigateur. C’est un crawler utilisant Guzzle et Symfony BrowserKit qui permet d’interagir avec les pages HTML.
En revanche, cette solution a des inconvénients, et pas des moindre, car les interactions sur une page web sont limitées aux simples clics sur des liens ou boutons de formulaires. Les champs d’un formulaire, quant à eux, ne peuvent être renseignés, ce qui peut être problématique pour valider une fonctionnalité.
- WebDriver
C’est un protocole permettant de piloter automatiquement les navigateurs (comme Chrome ou Firefox) via Selenium (recommandé par l’équipe de Codeception), PhantomJS ou ChromeDriver.
Codeception utilise la bibliothèque facebook/php-webdriver de Facebook comme implémentation PHP du protocole WebDriver.
Ces solutions peuvent être complémentaires et permettent de tester votre site web ou application.
Plus d’infos ici sur les tests d’acceptances
Les tests unitaires
Codeception se base sur PHPUnit pour les tests unitaires, donc si vous êtes un adepte de tests PHPUnit, vous êtes en terrain conquis. Le framework va simplement ajouter une couche d’abstraction qui va simplifier certaines tâches récurrentes.
Plus d’informations ici sur les tests unitaires.
Les tests fonctionnels
Les tests fonctionnels sont pratiquement identiques aux tests d’acceptances, ils sont écrits de la même manière, à une différence majeure : ceux-ci ne nécessitent pas de serveur Web. Concrètement, il suffit de définir les variables $_REQUEST, $_GET et $_POST avant d’exécuter un test.
Pour ce faire, Codeception permet de se connecter à différents frameworks PHP supportant les tests fonctionnels : Symfony, Laravel5, Yii2, Zend Framework et autres. Il faut simplement activer le module souhaité dans la configuration pour pouvoir l’utiliser.
Plus d’informations ici sur les tests fonctionnels.
On va pouvoir également pousser les tests, en se connectant à un SGBD. Codeception propose des modules pour se connecter à différentes bases de données et on va ainsi pouvoir compter le nombre d’éléments en base (après une insertion par exemple), ou vérifier qu’une donnée à bien été mise à jour.
Plus d’informations ici sur la gestion des données.
Les tests d’acceptances en pratique
Les exemples qui vont suivre sont spécifique à l’architecture mise en place mais peuvent servir comme référence pour votre cas d’utilisation.
Architecture de l’infrastructure :
Pour ma part, l’architecture était composée de : – 2 bases de données (Oracle, MariaDB) – 1 API (Symfony) – Plusieurs applications Web (Symfony, WordPress, PHP natif)
Les applications Web doivent passer par l’API pour consommer les données en base :
Pour éviter d’installer et de configurer Codeception sur l’ensemble des applications web, nous avons pris la décision de l’isoler sur un seul et unique serveur afin de centraliser l’automatisation des tests sur l’ensemble des plateformes.
Nous avons également une interaction directe avec nos différentes bases de données (pour s’assurer de la cohérence des valeurs modifiées sur celles-ci), ainsi qu’avec notre API (nous verrons en détail pourquoi).
Architecture du framework :
Nous avons donc autant de répertoire que d’applications pour bien faire la distinction entre les différents tests, même si certains vont beaucoup se ressembler.
Exemple : L’authentification sur les différentes applications
Toutes les applications s’interfacent avec l’API pour la phase d’authentification. Certaines règles de gestion sont communes à l’ensemble des applications et sont appliquées directement au niveau de l’API.
Mais ce n’est pas toujours le cas, prenons le cas d’un compte utilisateur qui se trouve en situation d’impayé, nous allons lui bloquer l’accès à certaines applications, et selon l’application, surcharger le message d’erreur.
Nous pourrons ainsi nous assurer que nous avons bien le comportement attendu sur chacune de nos applications à l’aide de tests d’acceptances.
Configuration :
Analysons en détails le fichier de configuration « /Api/test/acceptance.suite.yml » :
Nous avons 2 modules activés : « WebDriver » et « REST ».
Concernant la configuration de ces modules : – WebDriver : il faudra renseigner les informations sur le protocole utilisé (Selenium, PhantomJS ou ChromeDriver), et l’url d’accès à notre application – REST : il faut spécifier si on doit se baser sur un navigateur ou un Framework
Développement d’un test via Webservices
Maintenant que nous avons terminé de configurer notre projet, nous allons développer deux tests de webservices. Le premier sera l’authentification d’un utilisateur avec des données erronées et le second avec des données valides.
Classe « /Api/tests/acceptance/LoginTest.php »
Si nous décortiquons un peu plus en détails ces différentes méthodes, vous pourrez constater que nous avons effectué des appels en POST avec des en-têtes spécifiques et que nous avons pu vérifier non seulement le code HTTP et le format de retour mais également la structure de la réponse.
Développement d’un test via une interface web
Ces tests seront effectués sur l’url que vous aurez spécifié dans la configuration de WebDriver.
Dans notre premier test, nous validons le formulaire de login sans renseigner de valeurs, puis nous renseignons avec des valeurs erronées, afin de s’assurer que le comportement obtenu est bien celui attendu.
Pour ce qui est du second test, nous renseignons cette fois-ci un identifiant correct et vérifions que l’utilisateur est bien redirigé vers une autre page.
Je vous partage (seulement) 2 méthodes mais sachez que nous en avons mis en place 13 tests différents sur l’authentification (compte bloqué, compte expiré, compte sans permissions, etc…) Libre à vous de déterminer si oui ou non le test est pertinent.
Lancement des tests
Maintenant que nous avons développé quelques tests, nous allons voir ensemble comment les jouer. Il faut savoir que vous avez la possibilité de jouer l’ensemble des tests d’une classe :
cd /var/www/project php codecept.phar run tests/acceptance/LoginTest.php
Mais vous avez également la possibilité de ne jouer qu’un seul test. Pour ce faire il suffit de préciser le nom de cette méthode lors du l’exécution des tests :
cd /var/www/project php codecept.phar run tests/acceptance/LoginTest.php:tryLoginSuccess
Lorsque vos tests sont en succès vous aurez un retour console du type :
Acceptance Tests (2) -------------------------------------------------------------------------------------------- ✔ LoginTest: Try login error (0.50s) ✔ LoginTest: Try login success (0.53s) --------------------------------------------------------------------------------------------
En revanche si vous avez des erreurs, pour avez la possibilité d’ajouter l’option « -vvv » lors de l’exécution de la commande afin d’avoir un maximum de détails sur l’erreur. Le framework génère également une capture d’écran du navigateur lorsqu’une erreur est levée, ce qui vient compléter les erreurs de la console.
Aller plus loin dans les tests
Selon les sites, et les fonctionnalités à tester, nous n’avons pas besoin des mêmes données en base.
En effet, lorsque nous testons le workflow d’inscription, le fait que notre base de données soit peuplée ou non nous importe peu, contrairement au workflow d’authentification où là, la présence de données est obligatoire.
Nous avons pris le parti d’écrire des fixtures pour l’ensemble des tests d’acceptances. Chaque test possède ses propres fixtures associées. Celles-ci sont toutes présentes sur notre API puisque c’est la seule application qui est en mesure se connecter aux différentes bases de données.
Nous avons donc une méthode supplémentaire dans notre classe de test qui va exécuter ces fixtures sur notre serveur API distant (via une connexion SSH). Afin de pouvoir exécuter des commandes bash sur le serveur, il faut activer un module supplémentaire : Cli
Nous pouvons aller encore plus loin dans les tests car après avoir joué les fixtures, nous allons pouvoir vérifier que les données ont bien été insérées en base de données.
Pour ce faire il faudra également activer un autre module : « MutiDb » qui n’est pas natif au framework. Nous avons utilisé la librairie « iamdevice/codeception-multidb ».
Donc voilà à quoi ressemble notre fichier de configuration finale :
Nous allons pouvoir s’assurer de la cohérence des données présentes en base grâce à des méthodes comme :
Vous pourrez retrouvez l’ensemble des méthodes ici
Conclusion
La mise en place des tests d’acceptances a permis d’une part la montée en compétence de l’ensemble de l’équipe sur les fonctionnalités du métier, mais a surtout permis de jouer des tests de non-régression avant chaque livraison et nous avons ainsi augmenté drastiquement la robustesse et la pérennité de notre application.
En revanche la maintenance des tests a un coût et à chaque évolution sur un écran, il faut repasser sur le test ou les tests qui sont impactés.
Il est donc préférable de modifier 1 fois 5 champs de formulaire que 5 fois 1 seul champ… car le coût en temps n’est pas du tout comparable !
Mon retour d’expérience sur le sujet s’arrête là, je vous invite à lire la documentation officielle pour en apprendre plus sur le framework et les possibilités qu’il peut apporter à votre application.