Cypress est un framework de test qui a gagné en popularité ces dernières années et qui se retrouve aujourd'hui de plus en plus utilisé chez les développeurs et les testeurs QA.
Avec Cypress, l'écriture des tests se fait directement en JavaScriptLangage de scripting orienté objet et les nombreuses fonctions utilitaires proposées par le framework rendent plus facilement lisibles les tests pour des non-experts de la programmation web. Son interface visuelle et ses explications claires en cas d'échec rendent son utilisation accessible à quiconque voudrait tester son application. Il n'y a alors plus d'excuse pour ne pas délivrer une application fiable et robuste !
Pour l'installer dans un projet (React, Vue, Angular ou autre), rien de plus simple, il suffit d'installer la librairie avec la commande suivante :
npm install -D cypress
Cypress permet d'effectuer une grande variété de tests, mais la librairie a été développée pour permettre d'effectuer des tests de composants et des tests end-to-end.
Pour faire simple :
Les tests de composants seront généralement développés en parallèle du composant lui-même. Il est d'ailleurs parfaitement possible d'écrire les tests d'abord dans une logique TDD. A l'inverse, il vaudra mieux attendre une certaine stabilité de l'application pour commencer les tests end-to-end. On a pas très envie de toucher les tests à chaque fois qu'un bouton est déplacé ou qu'un composant est modifié.
Avant de mettre les mains dans le code, regardons un peu l'interface proposée par Cypress :
On remarque ici trois parties :
En cas d'échec du test, il est possible de cliquer sur une des étapes du test (dans la partie 2) pour automatiquement retrouver le rendu visuel de l'échec (dans la partie 3).
Allez, mettons un peu les mains dans le code pour voir à quoi ressemble un fichier de test Cypress.
Avant tout, écrivons un composant très simple avec un bouton et une case à cocher. Le bouton sera désactivé ou activé selon l'état de la checkbox :
// src/components/CheckboxButton.vue
<script setup lang="ts">
import { ref } from "vue";
const isDisable = ref(false);
</script>
<template>
<div>
<input type="checkbox" v-model="isDisable" />
<label for="checkbox">Cocher pour désactiver le bouton</label>
</div>
<button :disabled="isDisable">Clique ici !</button>
</template>
<style scoped>
button {
margin-top: 20px;
}
</style>
Voici le rendu qu'on aurait pour ce composant :
Le projet utilise ici le framework VueJS, mais il aurait été parfaitement possible d'utiliser n'importe quel framework web comme ReactReact est un framework de développement JavaScript populaire. ou AngularAngular est un framework de développement JavaScript populaire basé sur TypeScript..
En ouvrant Cypress en mode component (npx cypress open --component
), il est possible de créer un nouveau fichier de test directement à partir d'un de nos composants web. Voici à quoi ressemble ce fichier :
// src/components/CheckboxButton.cy.ts
import CheckboxButton from './CheckboxButton.vue'
describe('<CheckboxButton />', () => {
it('renders', () => {
// see: https://on.cypress.io/mounting-vue
cy.mount(CheckboxButton)
})
})
Le mot clé describe
va définir une section ou un bloc de tests et chaque test est ensuite représenté par le mot clé it
. Pour l'instant, grâce à ce fichier, nous nous assurons uniquement de la capacité du composant à être monté sur une page web.
Voici comment nous pourrions tester le comportement de notre composant, c'est-à-dire vérifier que notre bouton est bien désactivé lorsque la checkbox est cochée (et inversement) :
// src/components/CheckboxButton.cy.ts
import CheckboxButton from './CheckboxButton.vue'
describe('<CheckboxButton />', () => {
it('renders', () => {
cy.mount(CheckboxButton)
})
it('has button disabled when checkbox is checked', () => {
cy.mount(CheckboxButton)
cy.get('input[type="checkbox"]').check()
cy.get('button').should('be.disabled')
})
it('has button enable when checkbox is not checked', () => {
cy.mount(CheckboxButton)
cy.get('input[type="checkbox"]').uncheck()
cy.get('button').should('not.be.disabled')
})
})
Nous utilisons ici plusieurs commandes intégrées dans Cypress comme :
cy.mount
qui permet de monter le composant
cy.get
qui permet de récupérer un élément de la page à l'aide d'un sélecteur du DOM
cy.get(..).check
/ cy.get(...).uncheck
qui permet de checker / unchecker un input de type checkbox
cy.get(…).should
qui permet de vérifier que l'élément sélectionné possède certaines caractéristiques (ici l'attribut disabled
).
\
Voilà, ce n'était pas si compliqué ! :)
C'est là la force de Cypress : tester le comportement de nos composants tout en restant facilement lisible !
L'écriture de tests end-to-end fonctionne de façon similaire. Il est possible d'ouvrir une URLUniform Ressource Locator avec le mot-clé cy.visit(...)
, puis d'interagir avec les boutons, les inputs, les dropdowns et tous les éléments du DOM que vous voudrez en les sélectionnant à l'aide de sélecteurs que vous aurez choisis.
data-cy
Les composants sont rarement aussi petits que celui présenté plus haut. Il arrive très fréquemment d'avoir plusieurs éléments HTMLHTML (HyperText Markup Language) est un langage permettant de décrire le découpage d'une page web. similaires, comme plusieurs inputs de type checkbox ou plusieurs boutons. Il est donc nécessaire de sélectionner nos éléments sur la page de façon précise (en sélectionnant uniquement l'élément désiré) et durable dans le temps (qu'on n'aurait pas à changer à chaque modification du composant). Il existe pour cela plusieurs façons de faire.
Il est possible d'utiliser les class
des éléments HTML et même les id
pour être plus précis. Mais cela peut être risqué, il peut en effet arriver à l'avenir que ces classes et ces ids soient amenés à changer ou même à être supprimés.
Une bonne pratique consiste donc à rajouter un attribut sur nos balises HTML. Bien qu'il soit possible d'utiliser n'importe quel attribut, nous allons faire simple ici et utiliser l'attribut data-cy
.
Voici à quoi ressemblerait le code modifié :
// src/components/CheckboxButton.vue
<script setup lang="ts">
import { ref } from "vue";
const isDisable = ref(false);
</script>
<template>
<div>
<input type="checkbox" id="checkbox" v-model="isDisable" data-cy="my-checkbox" />
<label for="checkbox" data-cy="my-label">Cocher pour désactiver le bouton</label>
</div>
<button :disabled="isDisable" data-cy="my-button">Clique ici !</button>
</template>
<style scoped>
button {
margin-top: 20px;
}
</style>
Et voici comment nous pourrions sélectionner ces attributs :
// src/components/CheckboxButton.cy.ts
import CheckboxButton from './CheckboxButton.vue'
describe('<CheckboxButton />', () => {
it('renders', () => {
cy.mount(CheckboxButton)
})
it('has button disabled when checkbox is checked', () => {
cy.mount(CheckboxButton)
cy.get('[data-cy="my-checkbox"]').check()
cy.get('[data-cy="my-button"]').should('be.disabled')
})
it('has button enable when checkbox is not checked', () => {
cy.mount(CheckboxButton)
cy.get('[data-cy="my-checkbox"]').uncheck()
cy.get('[data-cy="my-button"]').should('not.be.disabled')
})
})
L'avantage, c'est qu'on gagne ici en stabilité et en pérennité, l'inconvénient, c'est qu'on perd alors en lisibilité.
Heureusement, Cypress nous permet d'ajouter des commandes personnalisées, et c'est exactement ce que nous allons faire ici avec une nouvelle commande getBySel
(pour get by selector
).
// cypres/support/commands.ts
Cypress.Commands.add("getBySel", (selector, ...args) => {
return cy.get(`[data-cy=${selector}]`, ...args);
});
Cela permet de créer un raccourci pour éviter d'avoir à saisir l'attribut data-cy
lorsque l'on souhaite récupérer un élément de la page.
Nous pouvons donc maintenant utiliser cette nouvelle commande Cypress dans notre code :
// src/components/CheckboxButton.cy.ts
import CheckboxButton from './CheckboxButton.vue'
describe('<CheckboxButton />', () => {
it('renders', () => {
cy.mount(CheckboxButton)
})
it('has button disabled when checkbox is checked', () => {
cy.mount(CheckboxButton)
cy.getBySel('my-checkbox').check()
cy.getBySel('my-button').should('be.disabled')
})
it('has button enable when checkbox is not checked', () => {
cy.mount(CheckboxButton)
cy.getBySel('my-checkbox').uncheck()
cy.getBySel('my-button').should('not.be.disabled')
})
})
Voilà un aperçu d'un premier test avec Cypress. Il existe un grand nombre de fonctions utilitaires qui vous permettront de récupérer et d'interagir plus facilement avec les éléments du DOM, mais pas uniquement. Vous pourrez aussi simuler le comportement de vos fonctions, écouter tout un tas d'évènements différents ou bien voyager dans le temps grâce aux stubs, spies et clocks (https://docs.cypress.io/guides/guides/stubs-spies-and-clocks)
Cypress nous permet donc d'écrire des tests rapidement et facilement pour tester nos composants web et des scénarios précis du comportement d'un utilisateur.
Selon notre projet, il peut arriver que certaines contraintes soient dictées et que nous voulions les tester. Nous pourrions vouloir tester notre code sur différentes dimensions d'appareil, sur différents navigateurs, avec différentes variables d'environnement ou même en ajoutant une contrainte sur la durée maximale de chargement de nos pages. Eh bien tout cela est possible avec Cypress de façon très simple, directement depuis le fichier de configuration. Je vous invite à aller voir la documentation ici : https://docs.cypress.io/guides/references/configuration
Enfin, comme un grand nombre de frameworks de tests, il est parfaitement possible d'intégrer vos tests Cypress à votre pipeline de déploiement pour être certain de ne jamais briser vos précédentes features ou vos précédents fix avec du nouveau code.
Si cet article vous a donné envie de tester Cypress pour votre prochain projet, n'hésitez plus et allez jeter un oeil à la documentation officielle ici : https://docs.cypress.io/guides/overview/why-cypress
Le lexique du bug et du débug : tous les termes et méthodes pour comprendre comment débugguer efficacement une application ou un programme informatique.
Pourquoi et comment écrire des tests unitaires ? Définition et implémentation dans une application Java Springboot
Kubernetes, c’est quoi ? Comment ça marche ? Définition, avantages et inconvénients.