Tests automatisés pour CI/CD

L'objectif de l'intégration et du déploiement continus (CI/CD) est de permettre aux équipes de développement de livrer fréquemment des logiciels fonctionnels aux utilisateurs, ce qui leur permet à la fois de fournir une valeur ajoutée et d'obtenir un retour d'information utile sur la façon dont leur produit est utilisé dans le monde réel. De nombreuses organisations ont adopté la pratique DevOps afin de faire face à la concurrence.

Toutefois, la pression commerciale pour une livraison plus rapide ne devrait pas diminuer la qualité de ce qui est produit. Après tout, vos utilisateurs attendent de vos logiciels qu'ils soient stables et fonctionnels, même lorsqu'ils réclament de nouvelles fonctionnalités. C'est pourquoi un processus de test automatisé fiable et exhaustif, qui vous donne confiance dans votre dernière version, est essentiel à votre pratique d'intégration et de livraison continues.

Pourquoi automatiser les tests CI/CD ?

Les tests sont essentiels pour assurer la qualité des logiciels et font depuis longtemps partie des pratiques de développement des logiciels.

Dans un contexte de cascade, l'étape de tests manuels ou du contrôle qualité avait lieu après que le code ait été développé et intégré, et le but était de vérifier si l'application se comportait conformément aux spécifications.

Dans un contexte de cascade, l'étape de tests manuels ou du contrôle qualité avait lieu après que le code ait été développé et intégré, et le but était de vérifier si l'application se comportait conformément aux spécifications. Par contraste, un processus d'automatisation de CI/CD permet une approche agile de cycles itératifs courts qui fournissent un retour d'information rapide et permettent de publier de petites et fréquentes mises à jour. Une partie essentielle de ces cycles itératifs courts consiste à effectuer des tests pour valider automatiquement que le nouveau code fonctionne et n'a rien cassé d'autre.

L'intégration continue implique de régulièrement valider les modifications du code dans le master ou le tronc commun, de déclencher un build le cas échéant et d'assurer la qualité logicielle à chaque fois. Pour vraiment profiter des avantages de l'intégration continue, les membres de votre équipe doivent s'efforcer de faire un commit des modifications au moins une fois par jour. Toutefois, même au sein d'une petite équipe, la réalisation manuelle de ce niveau de tests d'automatisation exigerait des ingénieurs en assurance qualité un effort considérable et impliquerait un travail très répétitif. C'est là qu'interviennent les tests automatisés.

Les tests automatisés sont la solution idéale pour les tâches répétitives et produisent des résultats plus cohérents que le processus manuel, car certains détails sont difficiles à identifier ou les vérifications risquent de perdre leur cohérence si certaines étapes sont répétées trop souvent.

En plus d'être plus rapide que l'exécution manuelle de tests équivalents, les tests automatisés peuvent être exécutés en parallèle, ce qui vous permet d'élargir vos processus d'assurance qualité (dans la mesure où votre infrastructure le permet) lorsque le temps est compté. Bien que la rédaction de tests automatisés implique un investissement de temps initial, elle est rapidement rentabilisée lorsque les membres de votre équipe effectuent régulièrement des modifications et mettent en production beaucoup plus fréquemment.

Bien que les tests automatisés éliminent beaucoup de tâches fastidieuses et répétitives, ils ne rendent pas votre équipe d'ingénierie d'assurance qualité obsolète. En plus de définir et de hiérarchiser les cas connexes, l'équipe d'AQ participe à la rédaction de tests automatisés, souvent en collaboration avec les développeurs. Les techniciens sont également nécessaires pour les sections qui ne peuvent pas être automatisées, comme nous en discuterons plus tard.

Quelle est la place des tests dans le processus de CI/CD ?

La réponse est que les tests ont lieu à plusieurs étapes tout au long du processus.

Si vous êtes novice en matière d'intégration et de déploiement continus, cela peut sembler excessif, mais le CI/CD et l'automatisation de l'AQ repose sur des boucles de feedback étroites qui permettent à votre équipe de détecter les problèmes le plus tôt possible.

Il est beaucoup plus facile de régler un problème peu de temps après son introduction, car cela évite la rédaction de davantage de code sur une mauvaise base. Il est également plus efficace pour votre équipe d'effectuer des modifications avant de passer à autre chose et de perdre le contexte.

De nombreux outils de test de build automatisés prennent en charge l'intégration avec les outils de CI/CD, de sorte que vous pouvez introduire les données de test dans le pipeline et exécuter les tests par étapes, des résultats étant fournis après chaque étape. Selon votre outil de CI, vous pouvez également choisir de faire passer un build à l'étape suivante en fonction des résultats des tests de l'étape précédente.

Pour tirer le meilleur parti de votre pipeline grâce aux tests automatisés, il est généralement judicieux d'ordonner vos tests de build pour que les plus rapides soient effectués en premier. Cela vous permet d'obtenir un retour plus rapide et d'utiliser plus efficacement les environnements de test, car vous pouvez vous assurer que les tests initiaux ont réussi avant d'effectuer des tests plus longs et plus complexes.

Pour déterminer les priorités en matière de création et d'exécution de tests automatisés, il est utile de réfléchir à la pyramide de tests.

Construire une pyramide de tests

La pyramide de tests est un moyen pratique de conceptualiser la manière de hiérarchiser les tests d'intégration continue dans un pipeline de CI/CD, à la fois en termes de quantité relative et d'ordre dans lequel ils sont effectués.

Définie à l'origine par Mike Cohn, la pyramide de tests présente les tests unitaires en bas, les tests de service au milieu et les tests d'interface utilisateur en haut.

Bien que la dénomination puisse ne pas être exacte, le principe est bon : commencer par une base solide de tests unitaires automatisés qui sont rapides et simples à exécuter, avant de passer à des tests à la fois plus complexes à écrire et plus longs à exécuter, et de terminer par un petit nombre de tests plus complexes. Quels types de tests de CI/CD devriez-vous considérer ? Examinons les différentes options.

Tests unitaires

Les tests unitaires constituent à juste titre la base de la pyramide de tests. Ces tests sont conçus pour garantir que votre code fonctionne comme vous l'attendez en traitant la plus petite unité de comportement possible. Pour les équipes qui ont décidé d'investir dans la rédaction de tests unitaires, les développeurs prennent généralement la responsabilité de les écrire au fur et à mesure qu'ils rédigent le code. Cela se fait automatiquement lorsque l'on pratique le développement piloté par les tests (TDD), mais le TDD n'est pas une exigence pour écrire des tests unitaires.

Si vous travaillez sur un système existant et n'avez pas encore investi dans des tests unitaires, les rédiger pour l'ensemble de votre base de code en partant de zéro est souvent un obstacle insurmontable. Bien qu'une large couverture avec des tests unitaires soit recommandée, vous pouvez commencer avec ce que vous avez déjà et l'enrichir au fil du temps. Une stratégie réaliste peut consister à ajouter des tests unitaires à tout morceau de code que vous touchez, garantissant ainsi que tout nouveau code est couvert et priorisant le code existant en fonction de ce avec quoi vous interagissez pendant le développement.

Tests d'intégration

Avec les tests d'intégration, vous vous assurez que plusieurs parties de votre logiciel interagissent entre elles comme prévu, comme par exemple l'interaction entre un code d'application et une base de données. Il peut être utile de subdiviser les tests d'intégration en deux groupes, les tests larges et les tests étroits. Dans les tests d'intégration étroits, l'interaction avec un autre module est testée en utilisant un doublon de test plutôt que le module réel, tandis que les tests d'intégration larges utilisent le composant ou le service réel.

En fonction de la complexité de votre projet et du nombre de services internes et externes impliqués, vous voudrez peut-être rédiger une couche de tests d'intégration étroits qui s'exécuteront plus rapidement que les tests d'intégration larges (car ils ne nécessitent pas que d'autres parties du système soient disponibles), et les faire suivre d'une série de tests d'intégration larges, ciblant potentiellement les zones plus prioritaires de votre système.

Tests de bout en bout

Également connus sous le nom de tests full-stack, les tests de bout en bout portent sur l'ensemble de l'application. Bien que ces tests puissent être effectués par l'intermédiaire de l'interface graphique, ils n'ont pas besoin de l'être ; un appel d'API peut également exercer plusieurs parties du système (les API peuvent également être testées avec des tests d'intégration). La pyramide de tests recommande de recourir à un nombre réduit de ces tests, non seulement parce qu'ils sont plus longs à réaliser, mais aussi parce qu'ils ont tendance à être fragiles.

N'importe quelle modification de l'interface utilisateur peut casser ces tests, entraînant un bruit inutile dans vos résultats de tests de build et des délais pour les mettre à jour. Il est payant de concevoir des tests de bout en bout avec soin, et en tenant compte de ce qui a déjà été couvert par des tests de build de niveau inférieur, afin qu'ils soient les plus utiles possible.

Tests de performance

Bien que la pyramide de tests ne fasse pas référence aux tests de performance, il est intéressant d'envisager de les inclure dans votre suite de tests automatisés, en particulier pour les produits où la stabilité et la rapidité sont des exigences essentielles.

Sous la rubrique générale des tests de performance, on trouve une série de stratégies de test conçues pour vérifier comment votre logiciel fonctionnera dans un environnement réel. Les tests de charge vérifient comment le système se comporte lorsque la demande augmente, tandis que les tests de stress dépassent délibérément l'utilisation prévue et les tests d'imprégnation (ou d'endurance) mesurent les performances sous une charge élevée continue.

Avec ces types de tests, l'objectif n'est pas seulement de confirmer que le logiciel fonctionnera avec des paramètres définis, mais aussi de tester comment il se comporte lorsque ces paramètres sont dépassés, idéalement en échouant gracieusement plutôt qu'en se crashant complètement.

Environnements de test

Les tests de performance et de bout en bout nécessitent des environnements très similaires à l'environnement de production et peuvent nécessiter des données de test de build. Pour qu'un régime de tests automatisés apporte la confiance dans le logiciel testé, il est important que les tests de build soient exécutés de la même manière à chaque fois, et cela implique de s'assurer que les environnements de test restent consistants d'une exécution à l'autre (bien qu'ils doivent être mis à jour pour correspondre à l'environnement de production lorsque des changements y sont apportés).

La gestion manuelle des environnements peut être un exercice chronophage, il vaut donc la peine d'envisager d'automatiser les étapes de création et de démantèlement des environnements de pré-production pour chaque nouveau build.

Utiliser les retours d'information

L'objectif de l'exécution de tests automatisés dans le cadre de votre pratique de CI/CD est d'obtenir un retour d'information rapide quant aux modifications que vous venez d'apporter, il est donc essentiel d'écouter et de répondre à ce retour d'information.

Les serveurs de CI s'intègrent généralement à des outils de test automatisés afin que vous puissiez visualiser tous les résultats en un seul endroit. Les équipes de développement combinent souvent l'affichage des derniers résultats sur un tableau de bord ou un radiateur d'informations avec des notifications automatiques vers des plateformes de communication, comme Slack, pour les tenir informés des performances du dernier build.

Lorsqu'un test échoue, le fait de comprendre à quelle zone de la base de code il se rapporte et de pouvoir visualiser toute information produite par le test, telle qu'une trace de pile, une valeur de sortie ou une capture d'écran, peut accélérer le processus permettant de trouver la cause du problème. Il vaut la peine de prendre le temps de concevoir les tests avec soin, pour que chacun d'entre eux n'ait qu'une chose à tester, et de les labelliser précisément pour que vous puissiez comprendre ce qui a échoué. Les tests d'intégration continue et les outils de CI qui fournissent des informations supplémentaires sur les échecs des tests peuvent également vous aider à repasser vos builds dans le vert plus rapidement.

Comme toujours, les outils et les pratiques ne constituent qu'une partie de l'équation. Une très bonne pratique d'automatisation de CI/CD nécessite une culture d'équipe qui reconnaît non seulement la valeur des tests de CI/CD automatisés, mais aussi l'importance de répondre rapidement aux tests échoués afin de maintenir le logiciel dans un état déployable.

Les tests CI/CD et automatisés marquent-ils la fin des tests manuels ?

Une erreur commune parmi les nouveaux venus dans le domaine du CI/CD est que les tests automatisés éliminent le besoin de tests manuels et d'ingénieurs d'assurance qualité.

Bien que l'automatisation de CI/CD libère du temps pour les membres de l'équipe d'assurance qualité, elle ne les rend pas obsolètes. Au lieu de passer du temps sur des tâches répétitives, les techniciens peuvent se concentrer sur la définition de cas relatifs, la rédaction de tests automatisés et l'application de leur créativité et de leur ingéniosité à des tests exploratoires.

Contrairement aux tests de build automatisés qui sont soigneusement programmés pour être exécutés par un ordinateur, les tests exploratoires ne nécessitent qu'un léger socle. L'intérêt des tests exploratoires est de trouver des choses que les tests planifiés et structurés ne permettent pas de découvrir. Essentiellement, vous recherchez des problèmes que vous n'avez pas encore considérés et pour lesquels vous n'avez pas encore rédigé de cas de test. Lorsque vous décidez des domaines à explorer, tenez compte à la fois des nouvelles caractéristiques et des domaines de votre système qui seraient les plus préjudiciables si quelque chose devait mal tourner en production.

Les tests exploratoires ne doivent pas se transformer en tests manuels et répétitifs ; l'intention n'est pas d'effectuer chaque fois la même série de tests. Lorsque des problèmes sont découverts lors des tests exploratoires, en plus de les résoudre, prenez le temps d'écrire un test automatisé afin que, s'il se reproduit, il soit détecté et ce, beaucoup plus tôt dans le processus. Afin d'utiliser efficacement le temps des testeurs, les tests manuels ne devraient avoir lieu qu'après la réussite de tous les tests automatisés.

Amélioration continue pour l'automatisation des tests

Les tests automatisés jouent un rôle central dans tout pipeline de CI/CD.

Bien que la rédaction de tests automatisés nécessite un investissement en temps et en énergie, les avantages d'un retour d'information rapide et de la visibilité de la capacité de déploiement du code font des tests automatisés un investissement vite rentabilisé. Mais la construction d'une suite de tests n'est pas quelque chose que l'on fait une fois pour toutes sans jamais y revenir.

Vos tests de build automatisés font partie intégrante de votre application au même titre que le reste de votre code, et doivent donc être maintenus pour garantir qu'ils restent pertinents et utiles. Ainsi, l'amélioration continue que vous appliquez à votre code s'applique également à vos tests.

En continuant à développer la couverture des tests automatisés pour les nouvelles fonctionnalités et en intégrant les résultats des tests exploratoires, votre suite de tests restera efficace et performante. Il est également utile de prendre le temps d'examiner les performances et de voir s'il vaut la peine de réorganiser ou de scinder des étapes de votre travail afin d'obtenir certaines informations plus rapidement.

Les outils de CI peuvent fournir diverses mesures pour vous aider à optimiser votre pipeline, tandis que des indicateurs de tests non fiables peuvent signaler des tests incertains susceptibles de créer une confiance ou une inquiétude injustifiées. Mais si les mesures peuvent vous aider à améliorer votre processus de test automatisé, il faut éviter le piège qui consiste à penser que la couverture des tests est un objectif en soi. Le véritable objectif est de livrer régulièrement des logiciels fonctionnels à vos utilisateurs. L'automatisation sert cet objectif en fournissant un retour d'information rapide et fiable afin que vous puissiez avoir confiance dans le déploiement de votre logiciel en production.