Les tests automatisés sont l'une des pierres angulaires de l'intégration et du déploiement continus. En intégrant des tests continus dans vos pipelines de DevOps, vous pouvez considérablement améliorer la qualité des logiciels que vous développez.
L'intégration et le déploiement continus (CI/CD) permettent d'effectuer des modifications fréquentes et incrémentales, de façon à obtenir régulièrement des retours sur vos produits ou services. Il est toutefois important de veiller à ce que la rapidité et la fréquence de livraison n'ait pas d'impact négatif sur la qualité de vos produits. Les utilisateurs attendent toujours de nouvelles fonctionnalités, mais ils veulent aussi que vos logiciels restent toujours stables et performants.
C'est pourquoi disposer d'un processus de tests automatisé fiable et exhaustif garantissant la qualité de vos derniers builds est un élément essentiel pour vos pratiques de CI/CD.
Les tests sont essentiels pour assurer la qualité des logiciels et font depuis longtemps partie des bonnes pratiques du développement de logiciels.
Dans un contexte de modèle en cascade, la phase de tests manuels ou d'assurance qualité intervient après que le code a été développé et intégré.
💡Remarque : la stratégie en cascade, ou modèle en cascade, est une approche linéaire et séquentielle du cycle de développement logiciel et de la gestion de projets. Il s'agit de l'un des tout premiers modèles de développement logiciel, qui se caractérise par un flux structuré suivant une progression logique à travers toutes les phases d'un projet, et dans lequel chaque phase doit être complètement achevé avant le début de la suivante.
L'un des inconvénients de cette méthodologie réside dans le risque associé à un manque de révision et de flexibilité, car elle ne permet de vérifier si le code fonctionne comme prévu que longtemps après qu'il a été écrit. Entre-temps, il est probable que de nombreuses lignes de code aient été ajoutées, ce qui rend la résolution des problèmes bien plus difficile. Cela a notamment pour conséquence de ralentir la livraison de nouvelles fonctionnalités et de correctifs de bugs.
À l'inverse, l'approche Agile favorise des cycles de développement courts et itératifs et courts, et permet donc de livrer de nouvelles versions plus fréquemment, d'obtenir régulièrement des retours de vos utilisateurs et de prendre des décisions éclairées sur les prochains builds. En soutien de cette approche, le CI/CD automatise les étapes entre l'écriture du code et sa publication, rendant ainsi l'ensemble du processus plus fiable et efficace.
La réussite de cette méthodologie de DevOps requiert de faire des commits, de créer des builds et de tester les modifications du code régulièrement – idéalement plusieurs fois par jour. Cependant, même dans une petite équipe, il n'est pas réaliste de s'attendre à ce qu'un ensemble complet de tests manuels puisse être réalisée au moins une fois par jour. C'est pourquoi les tests automatisés ont un rôle crucial dans tout pipeline de CI/CD.
Bien que l'écriture de tests automatisés prenne du temps au départ, ce travail est rapidement rentabilisé et donne des résultats appréciables, surtout lorsque vous commencez à faire des commits et à déployer des modifications plus fréquemment. Investir dans la mise en place de tests automatisés apporte plusieurs avantages significatifs, notamment :
Bien que les tests automatisés éliminent beaucoup de tâches fastidieuses et répétitives, le travail de votre équipe d'ingénierie en assurance qualité n'en conserve pas moins son utilité. En plus de définir et de hiérarchiser les cas connexes, l'équipe d'assurance qualité participe à la rédaction de tests automatisés, souvent en collaboration avec les développeurs. Les ingénieurs en assurance qualité interviennent également sur les parties des tests qui ne peuvent pas être automatisées, comme nous le verrons un peu plus tard.
Les tests automatisés interviennent à plusieurs étapes tout au long du pipeline.
Le CI/CD et l'automatisation de l'assurance qualité s'appuient d'assurance qualité s'appuient sur des boucles de rétroaction précises et rapides qui permettent aux équipes d’identifier les problèmes au plus tôt.
Il est beaucoup plus facile de résoudre un problème peu de temps après sa détection, car cela réduit le risque que beaucoup de code ait été écrit sur une mauvaise base. Il est également plus efficace pour votre équipe d'effectuer des modifications avant de passer à autre chose et d'oublier 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.
La pyramide des tests est un outil de priorisation des tests automatisés dans un pipeline de CI/CD qui permet de déterminer le nombre de tests requis et l'ordre dans lequel ils doivent être exécutés.
Définie à l'origine par Mike Cohn, la pyramide des tests présente les tests unitaires en bas, les tests de services au milieu et les tests d'interface utilisateur au sommet.
La pyramide des tests comprend les étapes suivantes :
Quels types de tests de CI/CD devriez-vous considérer ? Examinons les différentes options.
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. Par exemple, si vous créez une application météo, la conversion des valeurs des degrés Celsius à Fahrenheit peut faire partie d'une fonctionnalité plus large. Vous pouvez utiliser des tests unitaires pour vous assurer que la fonction de conversion de la température renvoie les résultats attendus pour une plage de valeurs. À chaque fois que vous modifiez une partie du code associé, vous pouvez utiliser ces tests unitaires pour confirmer que cet aspect spécifique fonctionne comme prévu sans avoir à créer de build ni à exécuter l'application à chaque fois.
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 ajouter au fur et à mesure qu'ils rédigent le code correspondant. Le développement piloté par les tests (TDD) formalise ce processus (comme nous allons le voir ci-dessous), mais il n'est pas indispensable pour écrire des tests unitaires.
Une autre approche consiste à s'assurer que les tests unitaires font partie de la définition de l'état « terminé » pour chaque tâche de développement et de vérifier que ces tests sont en place lors de la réalisation des révisions de code ou de l'utilisation des rapports de couverture du code.
Si vous travaillez sur un système existant et n'avez pas encore utilisé les tests unitaires, les rédiger pour l'ensemble de votre base de code en partant de zéro peut sembler être une tâche insurmontable. Bien qu'il soit préférable que votre code bénéficie d'une couverture par des tests unitaires la plus large possible, vous pouvez partir de ce dont vous disposez actuellement et procéder graduellement à des ajouts,
en convenant avec votre équipe de commencer à ajouter systématiquement des tests unitaires à tout élément de code avec lequel vous interagissez. Cette stratégie permet de garantir que tout nouveau code soit couvert et de prioriser la couverture du code existant en fonction des éléments avec lesquels vous interagissez le plus.
Les tests d'intégration permettent de s'assurer que les interactions entre différentes parties de votre logiciel, par exemple entre le code de l'application et une base de données ou les appels à une API, fonctionnent comme prévu.
Il peut être utile de subdiviser les tests d'intégration en deux groupes, les tests larges et les tests étroits. Avec l'approche étroite, on utilise un double de test à la place d'un module réel pour tester l'interaction avec un autre module. Les tests d'intégration étendus utilisent quant à eux les composant ou services réels. Pour reprendre l'exemple d'une application météo, un test d'intégration étendu pourrait récupérer les données de prévisions à partir d'une API, alors qu'un test étroit utiliserait une simulation de données.
Selon la complexité de votre projet et le nombre de services internes et externes impliqués, il peut être nécessaire de commencer par une couche de tests d'intégration étroits. Ces tests s'exécutent plus rapidement (et fournissent des retours plus rapides) que les tests d'intégration larges, car ils ne requièrent pas la disponibilité d'autres parties du système.
Si les tests étroits s'exécutent correctement, vous pouvez ensuite appliquer un ensemble de tests d'intégration larges. Dans la mesure où ces tests sont plus longs à exécuter et plus compliqués à maintenir, il est recommandé de les limiter aux zones prioritaires de votre produit ou service.
Également connus sous le nom de tests full-stack, les tests de bout en bout portent sur l'ensemble de l'application. Ils servent généralement à valider des cas d'usage métier, tels que la création d'un compte ou la réalisation d'une transaction par un utilisateur.
Si les tests automatisés de bout en bout peuvent être exécutés via l'interface graphique, cela n'est pas indispensable, car un appel d'API peut aussi exécuter différentes parties du système (bien que les API puissent également être vérifié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. Toute modification de l'interface utilisateur peut les interrompre, ce qui perturbe vos résultats de tests de builds et entraîne des délais supplémentaires pour les mettre à jour. Il est donc judicieux de concevoir les tests de bout en bout avec soin, en tenant compte de ce qui a déjà été couvert par des tests de niveau inférieur, afin qu'ils soient les plus utiles possible.
Le développement piloté par le comportement (BDD) est une approche agile qui encourage la collaboration entre les développeurs, les ingénieurs qualité et les intervenants non techniques ou commerciaux. Il s'agit d'une évolution de l'approche du développement piloté par les tests mettant l'accent sur le comportement du logiciel plutôt que sur son implémentation.
Le développement piloté par le comportement peut fournir une stratégie pertinente pour développer les tests d'intégration et les tests de bout en bout. Voici quelques-uns des principaux aspects de cette approche :
Bien que la pyramide des tests ne fasse pas référence aux tests de performances, il est intéressant de les envisager, en particulier pour les produits pour lesquels la stabilité et la rapidité sont des facteurs essentiels.
Le domaine des tests de performances inclut une gamme de stratégies de test conçues pour vérifier le comportement de votre logiciel dans un environnement réel :
Le but de ces types de tests n'est pas seulement de vérifier le bon fonctionnement du logiciel dans le cadre des paramètres définis, mais aussi de tester son comportement lorsque les seuils spécifiés sont dépassés et de s'assurer que tout fonctionne de manière optimale.
Les tests de performance et les tests de bout en bout requièrent des environnements les plus similaires possibles à l'environnement de production et peuvent nécessiter des données de tests de builds. Pour qu'un régime de test automatisé soit considéré comme fiable, il est important que les tests soient exécutés de la même façon à chaque fois. Cela implique de s'assurer que les environnements de test restent cohérents entre chaque exécution et qu'ils soient mis à jour de façon à refléter la version de production lorsque des modifications y sont déployées.
La gestion manuelle de ces environnements peut être chronophage. Automatiser le processus de création et de suppression des environnements de pré-production pour chaque nouveau build permet non seulement de gagner du temps, mais aussi de garantir que le régime de tests soit constant et fiable.
L'approche de développement piloté par les tests trouve son origine dans la méthode de programmation extrême. Avec le développement piloté par les tests, la première étape consiste à écrire la liste des cas de tests pour la fonctionnalité que vous voulez ajouter. Ensuite, travaillez sur un cas de test à la fois, en écrivant d'abord le scénario de test (échoué), puis en écrivant le code pour faire réussir le test. Enfin, vous refactorisez le code si nécessaire avant de passer au cas de test suivant. Ce processus correspond aux étapes de la méthode « Red, green, refactor » ou au principe « Make it work; make it right ».
L'un des principaux avantages du développement piloté par les tests est qu'il vous oblige à ajouter des tests automatisés pour tout nouveau code que vous écrivez. Ainsi, votre couverture de tests s'étend constamment, ce qui permet d'obtenir des retours rapides et réguliers à chaque fois que vous modifiez votre code. Les autres avantages du développement piloté par les tests incluent :
Le développement piloté par les tests est un moyen efficace d'étendre progressivement la couverture des tests automatisés pour prendre en charge votre processus de CI/CD. Toutefois, ce type de développement n'est pas indispensable pour une stratégie DevOps efficace, et vous pouvez maintenir des niveaux de couverture des tests automatisés élevés sans y recourir.
Le but de l'utilisation de tests automatisés dans le cadre de votre pratique de CI/CD est d'obtenir des retours rapides sur les modifications que vous venez d'apporter. Prendre connaissance de ces retours et y répondre constitue un élément essentiel du processus. Les bonnes pratiques suivantes vous aideront à tirer pleinement parti de votre utilisation des tests automatisés :
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.
Pour de nombreuses équipes, le point de départ des tests automatisés est une suite de tests unitaires que vous pouvez déclencher manuellement ou dans le cadre d'un pipeline d'intégration continue simple. Au fur et à mesure de l'évolution de votre culture DevOps, vous pourrez monter dans les étapes de la pyramide des tests en ajoutant des tests d’intégration, des tests de bout en bout, des tests de sécurité, des tests de performances, et plus.
Avec les tests continus, on effectue toute une suite de tests automatisés dans le cadre d'un pipeline de CI/CD. Ainsi, chaque ensemble de modifications de code est automatiquement soumis à une série de tests automatisés afin de découvrir les bugs aussi rapidement que possible.
Les phases initiales d'un processus de tests continus peuvent inclure des tests exécutés dans l'IDE avant même que les modifications fassent l'objet d'un commit. Pour les tests ultérieurs, les tests continus nécessitent généralement des environnements de test qui sont actualisés automatiquement dans le cadre du pipeline.
Un processus de tests continus entièrement automatisé vous permet d'avoir toute confiance dans les modifications et la qualité du code, tout en vous en accélérant les publications de vos logiciels. En soumettant votre logiciel à un régime de tests rigoureux, vous réduisez considérablement le risque de bugs. L'exécution automatique et continue de ce processus permet non seulement de travailler plus efficacement, mais aussi de déployer des correctifs urgents de façon rapide et fiable.
Bien que l'implémentation des tests continus prenne du temps, c'est un objectif que vous pouvez atteindre de façon incrémentale, en automatisant d'autres aspects de vos pipelines de CI/CD et en augmentant progressivement la couverture de vos tests.
Une idée fausse assez répandue parmi les personnes débutant avec le CI/CD est que les tests automatisés éliminent le besoin de recourir à tests manuels et à des ingénieurs en 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 perdre du temps sur des tâches répétitives, les ingénieurs en assurance qualité peuvent se concentrer sur la définition de cas de tests, la rédaction de tests automatisés et l'application de leur créativité et de leurs compétences aux tests exploratoires.
L'approche pour les tests exploratoires est assez souple, contrairement aux tests de builds automatisés qui sont soigneusement scriptés pour être exécutés par un ordinateur. L'intérêt des tests exploratoires est de trouver des choses qu'une approche basée sur des tests planifiés et structurés ne permettrait pas forcément 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 éléments à explorer, tenez compte à la fois des nouvelles fonctionnalités et des parties de votre système qui seraient les plus préjudiciables si quelque chose devait mal tourner en production. 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.
Les tests exploratoires ne doivent pas se transformer en tests manuels répétitifs. L'objectif n'est pas d'exécuter inlassablement le même ensemble de vérifications à chaque fois. Si des problèmes sont détectés pendant les tests exploratoires, vous devez à la fois corriger le problème et écrire au moins un test automatisé. De cette façon, si le problème se produit de nouveau, il sera détecté beaucoup plus tôt dans le processus.
La construction d'une suite de tests n'est pas quelque chose que l'on fait une fois pour toutes sans jamais y revenir. Les tests automatisés doivent être maintenus pour s'assurer qu'ils restent pertinents et utiles. Tout comme vous améliorez constamment votre code, vous devez améliorer continuellement vos tests.
En continuant à ajouter 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 de vos tests et de voir s'il est pertinent de réorganiser ou de scinder des étapes de votre processus de tests afin d'obtenir des retours plus rapidement.
Les outils de CI peuvent fournir diverses métriques pour vous aider à optimiser votre pipeline, tandis que des tests instables échouant ou réussissant de manière imprévisible génèrent des indicateurs non fiables, susceptibles de créer des inquiétudes ou une confiance infondées.
Mais si les métriques peuvent vous aider à améliorer votre processus de test automatisé, il faut éviter de tomber dans le piège de considérer la couverture des tests comme 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'informations rapide et fiable afin de s'assurer de la qualité du logiciel avant son déploiement en production.
Les tests automatisés jouent un rôle central dans tout pipeline de CI/CD. L'exécution automatique de tests permet d'obtenir des retours d'information rapides sur les modifications de votre code, ce qui rend le développement plus efficace, car il est plus facile de corriger des bugs détectés au plus tôt.
Il est recommandé de prioriser vos tests automatisés en fonction de leur durée d'exécution. Les tests unitaires doivent s'exécuter en premier, car ils fournissent les retours les plus rapides, suivis par les tests d'intégration et enfin, les tests de bout en bout. Si vous n'utilisez aucuns tests automatisés, le mieux est de commencer par les tests unitaires. Le développement piloté par les tests (TDD) est une pratique de développement éprouvée qui peut vous aider à améliorer et à maintenir la couverture par les tests unitaires.
À mesure que votre connaissance du DevOps évoluera, vous souhaiterez peut-être passer aux tests continus. Une partie de cette transition impliquera d'automatiser la création et la maintenance de vos environnements de test. Lorsque vous écrivez des tests automatisés de plus haut niveau, traitez en priorité les éléments posant le plus de risques. Cela peut nécessiter des tests de performances automatisés, tels que les tests de charge, de stress ou d'endurance. Les tests exploratoires manuels sont un bon moyen d'identifier les lacunes dans votre couverture de tests, afin de continuer d'améliorer votre processus de CI/CD.
TeamCity offre une prise en charge étendue des frameworks de tests et de nombreuses fonctionnalités pour l'automatisation des tests pour vous aider à tirer pleinement parti de votre processus de CI/CD.
La vitesse et la fiabilité sont essentiels pour une automatisation des tests efficace et TeamCity est optimisé pour offrir les deux. En plus de fournir des rapports de test détaillés pour vous aider à identifier rapidement la racine du problème, TeamCity met automatiquement en évidence les tests instables, ce qui vous donne l'assurance que seuls les véritables problèmes vous sont signalés. La réorganisation et la parallélisation intelligentes des tests permettent d'obtenir des résultats encore plus rapidement, et la fonctionnalité d'exécution à distance fournit des retours avant que vous effectuiez un commit.
Grâce à l'intégration de TeamCity avec vos outils de suivi, vos IDE, Slack et d'autres plateformes, vous recevez des notifications sur les tests échoués quel que soit l'endroit où vous travaillez. Enfin, la prise en charge intégrale des machines virtuelles et des conteneurs Docker vous permet d'automatiser la gestion de vos environnements de tests et d'implémenter des tests continus dans le cadre de votre processus de CI/CD.