Mise en place de Cypress.js dans Gitlab CI – partie 1
9 juin 2020
Cette article est divisé en deux parties. La première explique comment mettre en place des tests end-to-end dans une application très simple, en découvrant quelques fonctionnalités très pratiques de Cypress.js. La deuxième (en cours d’écriture) expliquera comment intégrer ces tests dans Gitlab CI, de la mise en place des jobs et pipelines aux différents workflows de tests que l’on utilise.
Passer de Nightwatch.js à Cypress.js
Cypress.js a été pour nous une révolution. Nous avons utilisé Nightwatch.js pendant un long moment. Nous en étions assez content, mais quand nous avons testé Cypress.js, il était assez clair pour nous qu’il était pertinent de changer, principalement pour deux raisons :
- Stabilité : nous avons eu beaucoup de mauvaises surprises (principalement des tests qui échouent une fois mais réussissent le reste du temps ou des problèmes d’interaction avec certains elements, click, type…) en utilisant différents frameworks de tests end-to-end. Cypress.js n’est pas parfait mais il est beaucoup plus stable, en grande partie parce qu’il n’utilise pas Selenium.
- Experience de développement : enregistrement de videos, progression des tests, logger, debugger, attente automatique, time travel, reload automatique, etc. Cypress.js possède une quantité d’outils considérable pour les développeurs, ce qui fait de son utilisation un véritable plaisir.
J’aime travailler sur des projets simples pour illustrer mes propos. On va donc utiliser ce (faux) site web pour explorer ce que peut faire Cypress.
Il est temps d’aider Jordan, un développeur curieux qui a décidé d’apprendre à tester son application. Jordan a choisi (sans aucune influence) de tester son application avec Cypress.js.
Premiers pas avec Cypress.js
Jordan a son site en production, prêt à être testé. La première chose qu’il doit faire est d’installer Cypress.
Il ajoute ensuite un nouveau script dans son package.json
pour pouvoir lancer ses tests.
Il peut ensuite lancer la commande :
Et voilà ! Jordan est prêt. L’installation n’est pas très complexe.
Note: Cypress.js génère beaucoup de tests. C’est utile pour voir à quoi ça ressemble, mais comme ce n’est pas vraiment le sujet. Jordan va supprimer tous les exemples qui ont été créés dans le dossier integration
.
Écrire son premier test
Jordan se demande :
Qu’est-ce qu’il faut que je teste ?
Certaines personnes ont beaucoup d’inspiration, d’autres beaucoup moins. Regardons déjà ce que fait ce site web pour savoir ce qu’il faut tester.
Jordan sait qu’une grande partie de son trafic arrive directement sur sa page d’accueil (car il utilise des outils analytics, c’est pas n’importe qui). En arrivant sur la page d’accueil, les utilisateurs choisissent ensuite de créer un compte ou de se connecter. Et c’est tout. Pas le site le plus intéressant, mais bon.
Je pense que si tous mes utilisateurs arrivent sur la page d’accueil, je devrais commencer par tester que ma page d’accueil est fonctionnelle.
Bien joué Jordan.
Avec un peu d’aide (vraiment pas beaucoup), Jordan crée un nouveau fichier qu’il appelle homepage.spec.js
et le remplit avec le contenu suivant :
cy.visit()
est utilisé pour aller sur une URL passée en paramètre.cy.get()
est utilisé pour récupérer un élément dans le DOM et.should('exist')
vérifie que cet élément existe bien dans le DOM.
Note: cy.get()
utilise les query selectors pour sélectionner les éléments ans le DOM. data-cy
est un data-attribute (data-*
) que l’on peut mettre sur n’importe quelle balise HTML (ou presque). Il est préférable d’utiliser des data-attributes plutôt que des ids ou des classes pour ne pas entrer en conflit avec les modifications liées à l’affichage de la page (changement de style par exemple). Points bonus pour les data-attributes qui servent aussi à expliquer pourquoi ils sont là, tous les data-cy sont là pour permettre à Cypress de sélectionner les éléments (cf.Cypress.js Best practices).
Jordan peut maintenant lancer son test directement depuis l’interface graphique de Cypress.
Jusqu’ici tout est bon.
Tests avec des interactions utilisateur
Ok, je peux maintenant savoir si ma page d’accueil s’affiche correctement. Il est temps de tester ma page de connexion.
Lorsqu’on souhaite tester des fonctionnalités qui sont dépendantes de données (comme la connexion par exemple), il faut être sûr que, par exemple pour la connexion, le compte qu’on utilise pour se connecter existe (ou n’existe pas si c’est ce qu’on souhaite tester). Le compte que Jordan va utiliser pour son test sera toujours le même et a été créé dans ce but : servir de compte de test.
Un autre exemple pourrait être un article. Si nous avions un blog, le but du test de l’article ne serait pas d’en prendre un au hasard depuis la page d’accueil. Nous voudrions garder toujours le même pour garder le test consistant. Revenons au site de Jordan.
Note: Ce site accepte toutes les adresses emails et tous les mots de passe (oui c’est un faux site).
Jordan doit trouver quel workflow de login il veut tester en premier.
Mes utilisateurs arrivent principalement sur ma page principale avant de se connecter, il faudrait donc que je teste le workflow suivant :
– l’utilisateur arrive sur la page d’accueil
– l’utilisateur navigue sur la page de connexion via le menu
– l’utilisateur entre son adresse e-mail et son mot de passe
– l’utilisateur est connecté
Et voici à quoi ressemble le test qu’il écrit :
Les commandes Cypress sont assez simples à comprendre si on parle un minimum anglais. Regardons ce que font ces nouvelles commandes :
Celle-ci permet de récupérer l’URL courante puis d’utiliser l’assertion should
pour vérifier que l’URL est bien égale ('eq')
au second paramètre.
type
et click
peuvent être utilisés avec get
pour taper dans un champ ou cliquer sur un élément.
Le test fonctionne sans soucis.
Je voudrais maintenant poser une question simple. En regardant le code au-dessus qui (je pense) n’est pas très difficile à produire, à quel point ces tests pourraient être utilisés pour votre site web sur vos fonctionnalités principales par rapport au temps nécessaire pour les mettre en place ?
Je pense personnellement qu’ils sont un excellent investissement. Je vais continuer cette article sur les fonctionnalités de Cypress, car je souhaite garder cette article structuré. Si j’étais à la place de Jordan, j’arrêterai de développer des suites de tests et j’essayerai d’intégrer les tests que j’ai déjà écrits dans ma CI.
Le plus important n’est pas de couvrir toutes les fonctionnalités de votre site web mais plutôt de couvrir les workflows les plus importants pour être sûr qu’ils ne cassent pas. On peut toujours ajouter des suites de tests plus tard. Retournons sur le site de Jordan.
Commandes personnalisées de Cypress
Je n’aime pas vraiment mettre mes URLs en dur dans mon code. Et si je veux avoir une configuration différente en fonction des mes environnements ? Et si mes URLs changent dans mon routeur ?
Ne t’inquiète pas Jordan, on va faire ça mieux. Imaginons qu’on utilise un routeur. Il serait bien plus simple de pouvoir écrire des instructions de navigation de cette façon plutôt que de taper les URLs :
On peut créer une commande personnalisée goTo
pour répondre à ce besoin. Commençons par créer un “faux” router. L’implémentation dépendra de votre router mais l’idée reste la même. Voici donc notre router services/router.js
:
Maintenant qu’on a un router, on peut l’utiliser dans notre fichier support/commands.js
.
Et on peut maintenant utiliser cette commande dans notre suite de tests (pas besoin d’utiliser require
pour récupérer la commande):
Cet exemple est simplifié car il ne prend pas en compte les paramètres du router et ce genre de chose, mais l’idée reste la même et il suffit simplement de passer les paramètres à la commande et de les réinjecter dans le router.
Les “tasks” de Cypress
Maintenant que ma connexion est testée, passons à mon inscription.
Mes utilisateurs tombent en général sur ma page d’accueil puis naviguent sur ma page d’inscription, je veux donc tester le workflow suivant :
– L’utilisateur arrive sur la page d’accueil
– L’utilisateur navigue sur la page d’inscription via le lien dans le menu
– L’utilisateur entre ses informations
– L’utilisateur renseigne le code qu’il a reçu par e-mail
– L’utilisateur est enregistré
Attention ! Ce workflow ressemble à celui de connexion mais il y a un piège.
– L’utilisateur renseigne le code qu’il a reçu par e-mail
On pourrait juste ouvrir un client Gmail et récupérer le code directement depuis le client pas vrai ? NON.
Premièrement, Cypress ne nous laisse pas changer de domaine. On peut toujours trouver un contournement, mais ce qu’il faut retenir, c’est qu’on ne veut pas passer par l’interface de Gmail car elle peut changer et donc casser nos tests.
Ce qu’on veut faire, c’est utiliser l’API de Gmail (ou une autre API) pour récupérer l’email, et extraire le code de son contenu pour ensuite l’injecter dans notre suite de tests. On peut faire ce genre de chose grâce aux tasks de Cypress.
Une task (ou tâche en français) est juste une fonction qui sera exécutée quand elle sera déclenchée. Ce qui la rend particulière est que cette fonction ne s’exécute pas dans le navigateur, mais dans un environnement Node.js, ce qui veut dire qu’on peut faire ce qu’on veut (plus ou moins), car on a accès à l’API de Node.js. On peut par exemple lire un fichier avec le package fs
…
… ou on peut installer un package qui permet de lire un email via une API et récupérer notre code dans l’email. 😃
Note: Puisque c’est un peu long à mettre en place, on ne va pas vraiment récupérer le code depuis Gmail. On utilisera à la place une “fausse” API qui renvoie le code (toujours le même). Si vous souhaitez le mettre en place, vous pouvez utiliser ce package et faire la même chose avec un peu plus de configuration.
Après avoir lu la documentation, Jordan écrit sa “task” dans le dossier plugins
en s’assurant d’installer axios
pour faire son appel API.
Puisqu’on retourne une promise, Cypress attendra que la promise soit résolue (ou rejetée).
Il peut maintenant utiliser sa “task” dans la suite de tests :
Et oui, Cypress tapera le code avant de cliquer sur le bouton submit.
Cette première partie était une introduction à Cypress.js. On peut faire beaucoup de choses avec cet outil. Rappelez-vous que le but n’est pas créer une usine de tests trop compliquée à maintenir. Démarrez avec peu de tests très stables et ajoutez des tests au fur et à mesure. Le plus important est de pouvoir tester les fonctionnalités qui ont beaucoup de valeurs et de faire confiance à ses tests.
Dans le prochain article, on réutilisera le code que l’on a produit et on l’intégrera dans Gitlab CI. Stay tuned.
Expert technique