Qu’est-ce que Cypress ?
Cypress est un outil de tests de bout en bout qui s’exécute à l’intérieur du navigateur, aux côtés de votre application. Ce choix architectural lui confère une expérience développeur exceptionnellement fluide : un runner interactif où vous pouvez observer l’exécution des commandes, la possibilité d’inspecter le DOM à chaque étape, et un débogueur avec fonction de “voyage dans le temps” qui rejoue chaque action.
C’est l’un des outils de test navigateur les plus populaires, particulièrement apprécié pour sa facilité de prise en main. Les commandes sont mises en file d’attente automatiquement, les assertions sont répétées jusqu’à ce qu’elles réussissent, et le runner rend évident l’origine d’un problème lorsqu’un test échoue.
Écrire un test
Les tests Cypress utilisent une API de commandes chaînables et une structure describe/it.
// cypress/e2e/todos.cy.js
describe("todos", () => {
beforeEach(() => {
cy.visit("/todos");
});
it("adds a todo", () => {
cy.get('[data-cy="new-todo"]').type("Write tests");
cy.get('[data-cy="add"]').click();
cy.get('[data-cy="todo"]').should("have.length", 1);
cy.contains("Write tests").should("be.visible");
});
});
Les commandes Cypress sont mises en file d’attente et exécutées dans l’ordre, même si elles retournent immédiatement. C’est pourquoi vous devez chaîner .then ou utiliser des alias plutôt que d’assigner le résultat d’une commande à une variable.
Commandes et assertions
Les commandes principales couvrent la navigation, les requêtes et l’interaction :
cy.visit(url)ouvre une page.cy.get(selector)effectue une requête via un sélecteur CSS.cy.contains(text)trouve un élément par son texte..type(),.click(),.select(),.check()interagissent avec les éléments..should()effectue une assertion et des tentatives de répétition (retries).
// assertions.cy.js
cy.get("form").should("be.visible");
cy.get('[data-cy="count"]').should("have.text", "3");
cy.get("button").should("be.disabled");
cy.get('[data-cy="row"]').should("have.length", 5);
Comme .should() réessaie jusqu’à ce que l’action réussisse ou que le délai d’expiration soit atteint, vous avez rarement besoin d’attendre explicitement. C’est cette même approche basée sur les tentatives qui rend les tests Playwright résilients, appliquée ici à une API chaînée.
Stubbing réseau avec cy.intercept
cy.intercept est le moyen de rendre vos tests déterministes. Il permet de stubber des réponses, d’espionner le trafic réel, de modifier des requêtes et de contrôler le timing.
// intercept.cy.js
cy.intercept("GET", "/api/users", {
statusCode: 500,
body: { message: "Server error" },
}).as("users");
cy.visit("/users");
cy.wait("@users");
cy.contains("Something went wrong").should("be.visible");
L’utilisation d’un alias avec cy.wait("@users") vous permet d’effectuer des assertions sur la requête et d’attendre la réponse sans avoir à deviner le délai. Stubber des états d’erreur de cette manière est bien plus fiable que de tenter de les provoquer depuis un backend réel.
Commandes personnalisées et fixtures
Les flux répétitifs doivent être regroupés dans une commande personnalisée, ce qui permet de garder les tests lisibles.
// cypress/support/commands.js
Cypress.Commands.add("login", (email, password) => {
cy.visit("/login");
cy.get('[data-cy="email"]').type(email);
cy.get('[data-cy="password"]').type(password);
cy.get('[data-cy="submit"]').click();
});
// usage
cy.login("[email protected]", "secret");
Les fixtures chargent des données statiques depuis cypress/fixtures, et cy.fixture("users.json").then(...) fournit aux tests des entrées prévisibles. Combinées avec cy.intercept, les fixtures sont la méthode standard pour tester sans backend actif.
Sélectionner des éléments
Cypress utilise des sélecteurs CSS, et la tentation est donc grande de lier vos tests au style. Évitez cela. Privilégiez, dans cet ordre :
- Les requêtes d’accessibilité via
@testing-library/cypress(findByRole,findByLabelText). cy.containspour le texte visible.- Un attribut
data-cydédié lorsqu’il n’y a pas d’identifiant visible pour l’utilisateur.
Un attribut data-cy est explicite et stable, ce qui permet aux tests de survivre aux refontes graphiques. Ce n’est pas le cas des sélecteurs basés sur les classes.
Où se place Cypress
Cypress constitue la couche de tests de bout en bout (end-to-end). Veillez à garder votre pyramide de tests équilibrée : des tests unitaires rapides avec Vitest ou Jest, des tests de composants avec Testing Library, et un ensemble ciblé de parcours de bout en bout dans Cypress ou Playwright. Poussez les cas limites (edge cases) vers les couches inférieures, moins coûteuses, et réservez l’e2e pour les flux critiques qui doivent impérativement fonctionner de bout en bout.
Bonnes pratiques
- Simulez le réseau avec
cy.interceptpour obtenir des tests déterministes. - Utilisez
data-cyou des requêtes accessibles plutôt que des sélecteurs de style. - Gardez vos tests indépendants afin qu’ils puissent être exécutés en parallèle.
- Encapsulez les flux répétitifs dans des commandes personnalisées.
- Connectez-vous via une commande personnalisée ou un appel API plutôt que par l’interface utilisateur à chaque fois.
- Effectuez vos assertions sur des résultats visibles par l’utilisateur, et non sur l’état interne.
- Exécutez vos tests en mode headless dans la CI et conservez le runner interactif pour le débogage local.
Erreurs courantes
- Attendre avec
cy.wait(milliseconds)au lieu d’utiliser un alias réseau ou une assertion. - Coupler les sélecteurs aux classes CSS et à la structure.
- Partager l’état entre les tests, provoquant des échecs dépendant de l’ordre d’exécution.
- Tenter de stocker les résultats de commandes dans des variables sans
.thenou sans alias. - Tout tester de bout en bout (end-to-end) au lieu de tester à la couche appropriée.
- Se connecter via l’interface utilisateur avant chaque test, ce qui ralentit la suite de tests.
Et après ?
Cypress est un outil convivial et puissant pour tester les parcours utilisateurs réels. Comparez-le avec Playwright pour une alternative multi-navigateurs, et construisez vos couches inférieures avec Vitest et Testing Library. Ajoutez ensuite une commande de connexion personnalisée ainsi qu’un flux critique, puis développez votre suite de tests à partir de là.