Intégration des tests Cypress avec Docker, Buildkite et CICD #frontend@twiliosendgrid

Publié: 2020-12-30

Nous avons écrit de nombreux tests Cypress de bout en bout (E2E) pour valider que nos applications Web fonctionnent toujours comme prévu avec le backend. Après avoir écrit ces tests d'automatisation du navigateur, nous aimerions toujours que ces tests Cypress s'exécutent ou soient déclenchés d'une manière ou d'une autre comme nos tests unitaires avant de fusionner le code et de le déployer dans certains environnements. Cela nous a amenés à vouloir exécuter nos tests Cypress dans un conteneur Docker pour les intégrer à notre fournisseur d'intégration continue (CI) et aux machines que nous utilisons dans le cloud pour exécuter ces conteneurs.

En ce qui concerne les flux de déploiement, nous utilisons Buildkite comme fournisseur de CI. Cela nous permet de générer une construction d'étapes automatisées pour notre application dans un pipeline Buildkite lorsque nous prévoyons de déplacer du code à tous les niveaux. Pour plus de contexte, un pipeline est un endroit généralement lié au référentiel d'une application où nous pouvons examiner les builds ou déclencher des builds avec certaines étapes à exécuter lorsque vous créez des demandes d'extraction, poussez de nouvelles modifications de code, fusionnez le code à maîtriser et déployez dans différents environnements. . Nous créons plusieurs pipelines à des fins distinctes, telles que le déploiement, les tests Cypress déclenchés et les tests Cypress spécifiques exécutés selon un calendrier.

Ce billet de blog part du principe que vous avez déjà écrit des tests Cypress et que certains tests sont en cours d'exécution, mais que vous souhaitez des idées sur la manière d'exécuter ces tests en permanence dans vos flux de développement et de déploiement. Si vous souhaitez plutôt un aperçu de l'écriture de tests Cypress, vous pouvez consulter ce billet de blog précédent, puis y revenir lorsque vous avez quelque chose à exécuter.

Notre objectif est de vous expliquer comment intégrer des tests Cypress dans un conteneur Docker avec votre fournisseur CI en examinant comment nous l'avons fait avec Docker Compose et Buildkite dans notre pipeline de déploiement. Ces idées peuvent être développées dans votre infrastructure pour les stratégies, les commandes et les variables d'environnement à appliquer lors du déclenchement des tests Cypress.

Notre flux CICD standard

Dans notre flux de développement et de déploiement standard, nous avons configuré deux pipelines :

  1. Le premier gère nos étapes de déploiement lorsque nous poussons le code.
  2. La seconde déclenche l'exécution en parallèle de nos tests Cypress et leur enregistrement. Le succès ou l'échec de cette opération affecte le pipeline de déploiement.

Dans notre pipeline de déploiement, nous développons nos actifs d'application Web, exécutons des tests unitaires et avons des étapes pour déclencher des tests Cypress sélectionnés avant le déploiement dans chaque environnement. Nous nous assurons qu'ils réussissent avant de supprimer la possibilité de déployer un bouton-poussoir. Ces tests Cypress déclenchés dans le deuxième pipeline s'exécutent également dans un conteneur Docker et sont connectés au tableau de bord Cypress payant via une clé d'enregistrement afin que nous puissions revenir sur les vidéos, les captures d'écran et la sortie de la console de ces tests Cypress pour déboguer tout problème.

À l'aide des entrées de sélection de Buildkite , nous avons conçu une dynamique, choisissez votre propre aventure afin que les utilisateurs puissent sélectionner "Oui" ou "Non" pour décider quels dossiers de spécifications Cypress exécuter et vérifier à mesure que nous poussons plus de code. La réponse par défaut serait "Non" pour toutes les options, mais la valeur "Oui" serait le chemin global vers le dossier de spécification Cypress.

Parfois, nous ne voulons pas exécuter tous les tests Cypress si notre changement de code n'affecte pas les autres pages. Au lieu de cela, nous ne voulons déclencher que les tests dont nous savons qu'ils seront affectés. Nous devrons peut-être également déployer une solution rapide en production pour un problème de bogue urgent, car nous nous sentons suffisamment en confiance pour ne pas exécuter nos tests Cypress, qui peuvent prendre de 0 à 10 minutes selon le nombre de tests que nous déclenchons. Nous fournissons un exemple à la fois visuellement et dans les étapes YML pour cette partie.

Ensuite, nous avons implémenté notre propre script Bash appelé runCypress.sh à exécuter après cette étape de sélection pour analyser les valeurs "Oui" ou "Non" sélectionnées. Nous faisons cela pour former une liste de chemins de spécification séparés par des virgules à exécuter et à ajouter en tant qu'option, --spec , à notre éventuelle commande Cypress qui s'exécute dans un conteneur Docker dans un pipeline déclenché. Nous exportons des variables d'environnement telles que la liste formée des spécifications dans "CYPRESS_SPECS" et l'environnement de test actuel dans "CYPRESS_TEST_ENV" à utiliser dans le pipeline que nous déclenchons à la fin du script avec buildkite-agent pipeline upload "$DIRNAME"/triggerCypress.yml .

Vous avez peut-être remarqué que nous exportons également une variable d'environnement "ASYNC". Dans Buildkite, vous pouvez choisir qu'une étape de construction déclenchée soit bloquante ou non bloquante en termes de succès ou d'échec. Si « ASYNC » est défini sur true, nos principales étapes de pipeline de déploiement continueront à s'exécuter et n'attendront pas que les tests Cypress déclenchés dans un autre pipeline se terminent. Le succès ou l'échec du pipeline n'affecte pas le succès ou l'échec du pipeline de déploiement.

Si « ASYNC » est défini sur false, nos principales étapes de pipeline de déploiement seront bloquées jusqu'à ce que les tests Cypress déclenchés dans un pipeline différent se terminent. Le succès ou l'échec de la génération déclenchée entraîne le succès ou l'échec global du pipeline de déploiement où il reprend ensuite.

Lorsque notre code est toujours dans une branche de fonctionnalité avec une demande d'extraction ouverte, nous aimons pousser plus de modifications, déclencher des tests Cypress et voir comment les choses se comportent. Cependant, nous ne voulons pas toujours bloquer l'exécution du reste des étapes du pipeline de déploiement si les tests déclenchés échouent, car il y a potentiellement plus de changements en cours de route. Dans ce scénario, nous définissons "ASYNC" sur false pour ne pas bloquer si les tests Cypress échouent. Pour le cas où nous avons déjà fusionné notre pull request dans master et déployé en staging mais que nous voulons déclencher des tests Cypress avant de déployer en production, nous définissons « ASYNC » sur true car nous voulons que les tests Cypress réussissent toujours avant de passer en production. .

En revenant à runCypress.sh , nous rappelons que le script déclenche l'exécution du deuxième pipeline en appelant le fichier triggerCypress.yml avec les valeurs de variable d'environnement affectées. Le fichier triggerCypress.yml ressemble à ceci. Vous remarquerez que l'étape "déclencheur" et l'interpolation des valeurs dans les messages de construction sont utiles pour le débogage et les noms d'étape dynamiques.

Que nous déclenchions l'exécution des tests Cypress à partir de notre pipeline de déploiement vers un pipeline de déclenchement distinct ou que nous exécutions les tests Cypress selon un calendrier dans un pipeline dédié, nous suivons et réutilisons les mêmes étapes tout en modifiant uniquement les valeurs des variables d'environnement.

Ces étapes impliquent :

  1. Construire l'image Docker avec une dernière balise et une balise de version unique
  2. Pousser l'image Docker vers notre registre privé
  3. Extraction de cette même image pour exécuter nos tests Cypress en fonction de nos valeurs de variable d'environnement dans un conteneur Docker

Ces étapes sont décrites dans un fichier pipeline.cypress.yml comme suit :

Lorsque nous déclenchons l'exécution des tests Cypress, une génération distincte est lancée dans le pipeline de déclenchement Cypress. En fonction du succès ou de l'échec de la construction, l'exécution du test Cypress bloquera ou nous permettra de déployer en production lorsque nous passerons de la mise en scène à la production pour les constructions de la branche principale.

En cliquant sur l'étape «Triggered cypress/integration/…», vous accéderez à la construction du pipeline déclenché avec une vue comme celle-ci pour voir comment les tests se sont déroulés.

Si vous êtes curieux de savoir comment la partie Docker est entièrement connectée, nos Dockerfile.cypress et docker-compose.cypress.yml utilisent ces variables d'environnement exportées de nos pipelines pour ensuite utiliser la commande Cypress appropriée du package.json de notre application pointant vers la droite environnement de test et exécutant les fichiers de spécifications sélectionnés. Les extraits ci-dessous montrent notre approche générale que vous pouvez développer et améliorer pour être plus flexible.


En dehors des tests exécutés lors de nos cycles habituels d'intégration et de déploiement, nous avons créé des pipelines Buildkite dédiés. Ces pipelines s'exécutent selon un calendrier pour des tests importants par rapport à notre environnement de staging afin de garantir que nos services frontend et backend fonctionnent correctement. Nous avons réutilisé des étapes de pipeline similaires, ajusté certaines valeurs de variables d'environnement dans les paramètres du pipeline Buildkite et configuré une planification cron pour qu'elle s'exécute à une heure planifiée. Cela nous aide à détecter de nombreux bogues et problèmes avec l'environnement de staging alors que nous continuons à surveiller la qualité de nos tests et si quelque chose en aval ou de nos propres poussées de code peut avoir conduit à l'échec des tests.

Parallélisation

Nous utilisons également l'indicateur de parallélisation pour tirer parti du nombre de machines AWS que nous pouvons faire tourner à partir de notre file d'attente d'agents de build configurée par notre équipe Ops. Avec cet indicateur de parallélisation, Cypress affiche automatiquement un certain nombre de machines en fonction du nombre que nous avons défini dans la propriété "parallélisme" de Buildkite.

Nous avons pu exécuter plus de 200 tests en 5 minutes environ pour l'un de nos dépôts d'application.

Il répartit ensuite tous les tests Cypress pour qu'ils s'exécutent en parallèle sur ces machines tout en conservant l'enregistrement de chacun des tests pour une exécution de build spécifique. Cela a considérablement augmenté nos temps d'exécution des tests !

Voici quelques conseils pour paralléliser vos tests Cypress :

  • Suivez les suggestions du service de tableau de bord pour le nombre optimal de machines et définissez le nombre de machines dans une variable d'environnement pour plus de flexibilité dans vos pipelines.
  • Divisé en fichiers de test plus petits, en particulier en divisant les tests plus longs en morceaux que nous pouvons mieux paralléliser entre les machines.
  • Assurez-vous que vos tests Cypress sont isolés et ne s'affectent pas ou ne dépendent pas les uns des autres. Lorsque vous traitez des flux liés à la mise à jour, à la création ou à la suppression, utilisez des utilisateurs et des ressources de données distincts pour éviter que les tests ne se chevauchent et ne se heurtent à des conditions de concurrence. Vos fichiers de test peuvent s'exécuter dans n'importe quel ordre, alors assurez-vous que ce n'est pas un problème lors de l'exécution de tous vos tests.
  • Pour Buildkite, n'oubliez pas de transmettre la valeur de la variable d'environnement Buildkite build ID dans l' --ci-build-id en plus de l'option parallel afin qu'il sache à quelle exécution de build unique s'associer lors de la parallélisation des tests sur les machines.

Réviser:

Afin de connecter vos tests Cypress à votre fournisseur CI tel que Buildkite, vous devrez :

  1. Créez une image Docker avec votre code d'application, en utilisant l'image de base Cypress nécessaire et les dépendances requises pour exécuter les tests dans un environnement Node sur certains navigateurs.
  2. Poussez votre image Docker vers un registre avec certaines balises
  3. Tirez la même image vers le bas dans une étape ultérieure
  4. Exécutez vos tests Cypress en mode sans tête et avec des clés d'enregistrement si vous utilisez le service de tableau de bord Cypress.
  5. Définissez différentes valeurs de variables d'environnement et connectez-les aux commandes que vous exécutez pour Cypress afin de déclencher des tests Cypress sélectionnés sur un certain environnement de test dans ces conteneurs Docker.

Ces étapes générales peuvent être réutilisées et appliquées aux tests Cypress exécutés selon un calendrier et à d'autres cas d'utilisation, tels que le déclenchement de tests à exécuter sur des navigateurs sélectionnés en plus de vos pipelines de déploiement. La clé consiste à tirer parti des capacités de votre fournisseur CI et à configurer vos commandes pour qu'elles soient flexibles et configurables en fonction des valeurs des variables d'environnement.

Configurez vos commandes pour qu'elles soient flexibles et configurables en fonction des valeurs des variables d'environnement.

Une fois que vos tests sont exécutés dans Docker avec votre fournisseur CI (et si vous payez pour le service de tableau de bord), vous pouvez tirer parti de la parallélisation de vos tests sur plusieurs machines. Vous devrez peut-être modifier les tests et les ressources existants afin qu'ils ne dépendent pas les uns des autres pour éviter que les tests ne se piétinent les uns les autres.

Nous avons également discuté d'idées que vous pouvez essayer par vous-même, telles que la création d'une suite de tests pour valider votre API backend ou le déclenchement de tests à exécuter sur un navigateur de votre choix. Il existe également d'autres façons de configurer l'intégration continue ici dans la documentation Cypress .

De plus, il est important d'exécuter ces tests Cypress pendant les flux de déploiement ou les intervalles planifiés pour vous assurer que vos environnements de développement fonctionnent comme prévu en permanence. Il y a eu d'innombrables fois où nos tests Cypress ont détecté des problèmes liés aux services backend en aval qui étaient en panne ou modifiés d'une manière ou d'une autre, se manifestant par des erreurs d'application frontale. Ils nous ont particulièrement sauvés des bogues inattendus dans nos pages Web après que nous ayons publié de nouveaux changements de code React.

Maintenir la réussite des tests et surveiller avec diligence les échecs des tests dans nos environnements de test entraînent moins de tickets de support et des clients plus satisfaits en production. Le maintien d'une suite saine et stable de tests Cypress en cours d'exécution lorsque vous appliquez de nouvelles modifications de code offre une plus grande confiance dans le bon fonctionnement des choses et nous vous recommandons, à vous et à vos équipes, de faire de même avec vos tests Cypress.

Pour plus de ressources sur les tests Cypress, consultez les articles suivants :

  • Éléments à prendre en compte lors de la rédaction de tests E2E
  • Vue d'ensemble de 1 000 pieds des tests d'écriture Cypress
  • TypeScript Toutes les choses dans vos tests Cypress
  • Gestion des flux de messagerie dans les tests Cypress
  • Idées pour configurer, organiser et consolider vos tests Cypress