Comment faire vos premiers pas avec l'intégration continue ?

Découvrez comment adopter l'intégration continue et les tests automatisés en cinq étapes.

Sten Pittet Sten Pittet

L'intégration continue (CI) est une bonne pratique Agile et DevOps dans le cadre de laquelle les développeurs intègrent très tôt et très fréquemment leurs changements de code dans la branche principale ou dans un dépôt de code. L'objectif est de réduire le risque de vivre un véritable « enfer de l'intégration » en attendant la fin d'un projet ou d'un sprint pour merger le travail de tous les développeurs. Étant donné que la CI automatise le déploiement, elle aide les équipes à répondre aux exigences métier, à améliorer la qualité du code et à accroître la sécurité.

L'un des principaux avantages de la CI ? Elle vous fera gagner du temps au cours de votre cycle de développement en permettant l'identification et la résolution précoces des conflits. C'est également un excellent moyen de réduire le temps consacré à la correction des bugs et à la régression en mettant davantage l'accent sur une bonne suite de tests. Enfin, elle permet de partager une meilleure compréhension de la base de code et des fonctionnalités que vous développez pour vos clients.

La première étape de votre cheminement vers l'intégration continue : la configuration de tests automatisés.

Se lancer avec les tests automatisés

Comprendre les différents types de tests

Pour profiter pleinement des avantages de l'intégration continue, vous devrez automatiser vos tests afin de pouvoir les exécuter pour chaque changement apporté au dépôt principal. Nous insistons sur l'importance d'exécuter les tests sur chaque branche de votre dépôt, et pas uniquement sur la branche principale. De cette façon, vous serez en mesure de détecter rapidement les problèmes et de limiter les perturbations pour votre équipe.

Il est possible d'implémenter de nombreux types de tests, mais il n'est pas nécessaire de tout faire en même temps si vous débutez. Vous pouvez commencer modestement avec des tests unitaires et étendre votre couverture au fil du temps.

  • Les tests unitaires ont un périmètre restreint et vérifient généralement le comportement de méthodes ou de fonctions individuelles.
  • Les tests d'intégration permettent de s'assurer de la bonne cohabitation des composants. Cela peut impliquer plusieurs classes, ainsi que de tester l'intégration avec d'autres services.
  • Les tests d'acceptation sont similaires aux tests d'intégration, mais ils se concentrent sur les business cases plutôt que sur les composants eux-mêmes.
  • Les tests d'interface utilisateur permettent de s'assurer que l'application fonctionne correctement du point de vue de l'utilisateur.

Cependant, tous les tests ne se valent pas. Vous pouvez visualiser les compromis que vous ferez grâce à la pyramide de tests élaborée par Mike Cohn.

Triangle de test

Les tests unitaires sont rapides et peu coûteux à implémenter, car ils vérifient principalement de petits morceaux de code. En revanche, les tests d'interface utilisateur sont complexes à implémenter et lents à exécuter, car ils nécessitent souvent le démarrage d'un environnement complet ainsi que de multiples services pour émuler les comportements du navigateur ou du mobile. Pour cette raison, il est préférable de limiter le nombre de tests d'interface utilisateur complexes et de s'appuyer sur de bons tests unitaires à la base pour obtenir un build rapide et donner du feedback aux développeurs le plus tôt possible.

Exécutez vos tests automatiquement

Pour adopter l'intégration continue, vous devrez exécuter vos tests sur chaque changement qui est pushé vers la branche principale. Pour cela, vous aurez besoin d'un service capable de surveiller votre dépôt et d'identifier les nouveaux pushs vers la base de code. De nombreuses solutions s'offrent à vous, sur site ou dans le cloud. Vous devrez prendre en compte les éléments suivants pour choisir votre serveur :

  • Où est hébergé votre code ? Le service de la CI peut-il accéder à votre base de code ? Avez-vous une restriction particulière sur le lieu d'hébergement du code ?
  • De quel système d'exploitation et de quelles ressources avez-vous besoin pour votre application ? L'environnement de votre application est-il pris en charge ? Pouvez-vous installer les bonnes dépendances pour builder et tester votre logiciel ?
  • De quelle quantité de ressources avez-vous besoin pour vos tests ? Certaines applications cloud peuvent poser des restrictions quant aux ressources que vous pouvez utiliser. Si votre logiciel consomme beaucoup de ressources, vous voudrez peut-être héberger votre serveur de CI derrière votre pare-feu.
  • Combien de développeurs travaillent dans votre équipe ? Lorsque votre équipe utilise la CI, de nombreux changements sont pushés vers le dépôt principal chaque jour. Pour que les développeurs obtiennent un feedback rapide, vous devez réduire le temps d'attente pour les builds et vous voudrez utiliser un service ou un serveur qui vous fournit des accès simultanés suffisants.

Par le passé, vous deviez généralement installer un serveur de CI distinct comme Bamboo ou Jenkins. Aujourd'hui, vous pouvez trouver des solutions dans le cloud qui sont beaucoup plus simples à adopter. Par exemple, si votre code est hébergé sur Bitbucket Cloud, vous pouvez utiliser la fonctionnalité Pipelines dans votre dépôt pour exécuter des tests sur chaque push sans avoir besoin de configurer un serveur distinct ou des agents de build, et sans restrictions liées à la simultanéité.

image: node:4.6.0 pipelines:   default:     - step:         script:           - npm install           - npm test

Exemple de configuration pour tester un dépôt Javascript avec Bitbucket Pipelines.

Utilisez la couverture du code pour rechercher le code non testé

Une fois que vous avez adopté les tests automatisés, il est bon de les coupler avec un outil de couverture des tests qui vous donnera une idée de la couverture de votre base de code par votre suite de tests.

Il est recommandé de viser une couverture supérieure à 80 %, mais attention à ne pas confondre pourcentage de couverture élevé et qualité de la suite de tests. Un outil de couverture du code vous permettra de rechercher le code non testé, mais c'est bien la qualité de vos tests qui fera la différence à la fin de la journée.

Si vous débutez, ne vous précipitez pas pour atteindre une couverture de 100 % de votre base de code. Utilisez plutôt un outil de couverture des tests pour trouver les parties critiques de votre application qui ne font pas encore l'objet de tests et commencez par là.

Le refactoring est une opportunité d'ajouter des tests

Si vous êtes sur le point d'apporter des changements importants à votre application, vous devriez commencer par écrire des tests d'acceptation autour des fonctionnalités qui pourraient être affectées. Vous disposerez ainsi d'un filet de sécurité pour vous assurer que le comportement d'origine n'a pas été affecté après le refactoring du code ou l'ajout de nouvelles fonctionnalités.

Adoption de l'intégration continue

Si l'automatisation des tests est un élément clé de la CI, elle n'est pas suffisante en soi. Vous devrez peut-être revoir la culture de votre équipe afin de vous assurer que les développeurs ne travaillent pas pendant des jours sur une fonctionnalité sans merger leurs changements dans la branche principale. Vous devrez également mettre en place une culture du « build fonctionnel ».

Favorisez des intégrations précoces et fréquentes

Que vous utilisiez un développement basé sur « trunk » ou sur des branches de fonctionnalités, il est important que les développeurs intègrent leurs changements le plus rapidement possible dans le dépôt principal. Si vous laissez le code traîner trop longtemps sur une branche ou sur le poste de travail du développeur, vous vous exposez au risque d'avoir trop de conflits à examiner lorsque vous déciderez de procéder au merge dans la branche principale.

Si vous favorisez une intégration précoce, vous réduisez le périmètre des changements, ce qui permet de mieux comprendre les conflits lorsqu'ils surviennent. L'autre avantage est que cela facilite le partage de connaissances entre les développeurs, car les changements seront plus faciles à digérer.

Si vous effectuez des changements susceptibles d'avoir un impact sur une fonctionnalité existante, vous pouvez utiliser des flags de fonctionnalités pour désactiver vos changements en production jusqu'à ce que votre travail soit terminé.

Maintenez un build fonctionnel en permanence

Si un développeur rompt le build de la branche principale, la correction de ce problème devient la priorité principale. Plus les changements sont nombreux dans le build alors qu'il est non fonctionnel, plus il vous sera difficile de comprendre la cause du problème, et vous risquez en outre d'introduire davantage de dysfonctionnements.

Cela vaut la peine de consacrer du temps à votre suite de tests pour vérifier qu'elle peut rapidement se solder par un échec et donner un feedback au développeur qui a pushé les changements dès que possible. Vous pouvez diviser vos tests de manière à ce que les tests les plus rapides (les tests unitaires par exemple) soient exécutés avant les tests les plus longs. Si votre suite de tests nécessite toujours beaucoup de temps avant d'échouer, vous ferez perdre beaucoup de temps aux développeurs, qui devront changer de contexte pour revenir à leur tâche précédente et corriger le problème.

N'oubliez pas de définir des notifications afin de vous assurer que les développeurs sont alertés en cas de défaillance du build. Vous pouvez également aller plus loin en affichant l'état de vos branches principales sur un tableau de bord visible par tous.

Écrivez des tests dans le cadre de vos stories

Enfin, vous devrez veiller à ce que chaque fonctionnalité développée fasse l'objet de tests automatisés. Cela peut donner l'impression que vous allez ralentir le développement, mais en fait, cela va réduire considérablement le temps que votre équipe passera à corriger les régressions ou les bugs introduits à chaque itération. Vous pourrez également apporter des changements à votre base de code en toute confiance, car votre suite de tests pourra rapidement vérifier que toutes les fonctionnalités développées précédemment fonctionnent comme prévu.

Pour écrire de bons tests, vous devrez vous assurer que les développeurs sont impliqués dès le début de la définition des user stories. C'est un excellent moyen d'obtenir une meilleure compréhension partagée des exigences métier et de faciliter les relations avec les responsables produit. Vous pouvez même commencer par écrire les tests avant d'implémenter le code qui les exécutera.

Écrivez les tests lors de la correction des bugs

Que vous disposiez d'une base de code existante ou que vous débutiez, il est certain que des bugs surviendront dans le cadre de vos livraisons. Veillez à ajouter des tests lorsque vous les résolvez afin d'éviter qu'ils ne se reproduisent.

La CI permettra à vos ingénieurs de QA d'évoluer en matière de qualité

Le rôle de l'ingénieur de QA évoluera lui aussi avec l'adoption de la CI et de l'automatisation. L'ingénieur n'aura en effet plus besoin de tester manuellement des fonctionnalités banales de votre application et pourra désormais consacrer plus de temps à fournir des outils pour soutenir les développeurs et les aider à adopter les bonnes stratégies de test.

Une fois que vous aurez commencé à adopter l'intégration continue, vos ingénieurs de QA pourront se concentrer sur la simplification des tests grâce à des outils et des ensembles de données plus performants. Ils pourront également aider les développeurs à améliorer leur capacité à écrire un meilleur code. Il y aura toujours des tests exploratoires pour les cas d'usage complexes, mais cela devrait constituer une partie moins importante de leur travail.

L'intégration continue en cinq étapes

Vous devriez maintenant avoir un bon aperçu des concepts qui se cachent derrière l'intégration continue et nous pouvons les résumer de la manière suivante :

  1. Commencez par rédiger des tests pour les parties critiques de votre base de code.
  2. Utilisez un service de CI pour les exécuter automatiquement à chaque push vers le dépôt principal.
  3. Assurez-vous que votre équipe intègre ses changements au quotidien.
  4. Corrigez le build dès qu'il est défectueux.
  5. Écrivez des tests pour chaque nouvelle story que vous implémentez.

Bien que cela puisse sembler facile, votre équipe devra être totalement impliquée pour être efficace. Vous devrez ralentir vos livraisons au début et vous aurez besoin de l'appui des responsables produit pour vous assurer qu'ils ne pressent pas les développeurs à livrer des fonctionnalités sans effectuer de tests.

Nous vous recommandons de commencer par des tests simples pour vous habituer à la nouvelle routine avant d'implémenter une suite de tests plus complexes qui pourrait être difficile à gérer.