Logo Cypress

Cypress

Le framework de test end-to-end moderne qui permet de créer des tests fiables et rapides pour les applications web, avec une expérience développeur exceptionnelle et un système de debugging visuel puissant.

Pour les non-initiés

Qu'est-ce que Cypress ?

Dans le monde des sites web et des applications, comment être sûr que tout fonctionne correctement avant de livrer aux utilisateurs ? C'est là qu'intervient Icône CypressCypress - un outil qui permet de créer facilement des "robots testeurs" qui vont vérifier automatiquement que votre site web fonctionne comme prévu.

Imaginez Cypress comme un assistant virtuel ultra rapide qui peut, en quelques secondes, parcourir votre site web, remplir des formulaires, cliquer sur des boutons, et vérifier que tout s'affiche correctement - exactement comme le ferait un utilisateur réel, mais de manière automatisée et beaucoup plus rapide.

Ce que Cypress apporte à votre projet

Confiance dans les livraisons

La certitude que vos nouvelles fonctionnalités n'ont pas cassé les fonctionnalités existantes, permettant de livrer de nouvelles versions plus rapidement et en toute sécurité.

Détection rapide des bugs

Identification immédiate des problèmes pendant le développement, avant même qu'ils n'atteignent les utilisateurs finaux, réduisant les coûts de correction.

Gain de temps et d'argent

Automatisation des tests répétitifs qui prendraient des heures à réaliser manuellement, libérant les équipes pour des tâches à plus forte valeur ajoutée.

Meilleure expérience utilisateur

Garantie que les fonctionnalités critiques comme les formulaires, les paniers d'achat ou les processus d'inscription fonctionnent parfaitement pour tous les utilisateurs.

En résumé, Cypress est comme une police d'assurance pour votre site web ou application - il vérifie en permanence que tout fonctionne correctement, vous alertant immédiatement si quelque chose ne va pas, et vous donnant ainsi la tranquillité d'esprit nécessaire pour améliorer et faire évoluer votre produit en toute confiance.

Pour les développeurs

Fonctionnement technique

Icône CypressCypress est un framework de test end-to-end basé sur Icône JavaScriptJavaScript qui s'exécute directement dans le navigateur. Contrairement à d'autres outils de test qui opèrent via des pilotes de navigateur externes, Cypress fonctionne dans le même cycle d'exécution que l'application testée, offrant ainsi un accès sans précédent à tous les objets de l'application.

Architecture et concepts clés

Cypress possède une architecture unique qui lui confère plusieurs avantages par rapport aux frameworks de test traditionnels :

  • Exécution dans le navigateur - Cypress s'exécute directement dans le même contexte que l'application testée, permettant un accès direct au DOM, aux objets window, document, etc.
  • Contrôle du réseau - Cypress peut intercepter, stubber et contrôler le trafic réseau, facilitant les tests avec des données simulées
  • Architecture asynchrone - Toutes les commandes Cypress s'exécutent de manière asynchrone avec des attentes automatiques intelligentes
  • Time Travel - L'interface de Cypress permet de voir l'état de l'application à chaque étape du test
  • Debugging visuel - Les instantanés avant/après chaque action permettent un débogage immédiat

Exemples pratiques

Test d'authentification de base

Voici un exemple simple de test Cypress qui vérifie le processus de connexion d'un utilisateur :

Test d'authentification avec Cypress
// cypress/e2e/authentification.cy.js describe('Authentification', () => { beforeEach(() => { // Visite la page d'accueil avant chaque test cy.visit('/'); }); it('Permet à un utilisateur de se connecter avec des identifiants valides', () => { // Clique sur le bouton de connexion cy.contains('Connexion').click(); // Vérifie qu'on est bien sur la page de login cy.url().should('include', '/login'); // Remplit le formulaire de connexion cy.get('input[name="email"]').type('utilisateur@exemple.fr'); cy.get('input[name="password"]').type('motdepasse123{enter}'); // Vérifie que l'utilisateur est bien connecté cy.url().should('include', '/tableau-de-bord'); cy.contains('Bienvenue, utilisateur@exemple.fr').should('be.visible'); // Vérifie que le menu utilisateur est affiché cy.get('.user-menu').should('exist'); }); it('Affiche un message d'erreur avec des identifiants invalides', () => { // Clique sur le bouton de connexion cy.contains('Connexion').click(); // Remplit le formulaire avec des identifiants incorrects cy.get('input[name="email"]').type('utilisateur@exemple.fr'); cy.get('input[name="password"]').type('mauvais_mot_de_passe{enter}'); // Vérifie que l'erreur est affichée cy.contains('Identifiants invalides').should('be.visible'); // Vérifie qu'on reste sur la page de login cy.url().should('include', '/login'); }); });

Commandes personnalisées

L'une des forces de Cypress est la possibilité de créer des commandes personnalisées pour factoriser les opérations courantes :

Définition de commandes personnalisées dans Cypress
// cypress/support/commands.js Cypress.Commands.add('login', (email, password) => { // Cette commande personnalisée permet de se connecter rapidement cy.session([email, password], () => { cy.visit('/login'); cy.get('input[name="email"]').type(email); cy.get('input[name="password"]').type(password); cy.get('form').submit(); cy.url().should('include', '/tableau-de-bord'); }); }); Cypress.Commands.add('createProduct', (productData) => { // Cette commande crée un produit via l'API cy.request({ method: 'POST', url: '/api/products', body: productData, headers: { 'Authorization': `Bearer ${Cypress.env('API_TOKEN')}` } }).then((response) => { expect(response.status).to.eq(201); return response.body.id; // Retourne l'ID du produit créé }); }); Cypress.Commands.add('deleteProduct', (productId) => { // Cette commande supprime un produit via l'API cy.request({ method: 'DELETE', url: `/api/products/${productId}`, headers: { 'Authorization': `Bearer ${Cypress.env('API_TOKEN')}` } }).then((response) => { expect(response.status).to.eq(200); }); }); // Usage dans les tests: // cy.login('utilisateur@exemple.fr', 'motdepasse123'); // cy.createProduct({ name: 'Test Product', price: 19.99 }).then(productId => { // // faire quelque chose avec le produit // cy.deleteProduct(productId); // });

Fixtures et interception réseau

Cypress peut simuler des réponses API grâce aux fixtures et à l'interception des requêtes réseau :

Utilisation des fixtures et intercepteurs dans Cypress
// Exemple de fixture: cypress/fixtures/products.json { "products": [ { "id": 1, "name": "Smartphone XYZ", "price": 599.99, "category": "Électronique", "inStock": true, "imageUrl": "/images/products/smartphone-xyz.jpg" }, { "id": 2, "name": "Casque Audio Premium", "price": 199.99, "category": "Accessoires", "inStock": true, "imageUrl": "/images/products/casque-premium.jpg" }, { "id": 3, "name": "Tablette Ultra", "price": 399.99, "category": "Électronique", "inStock": false, "imageUrl": "/images/products/tablette-ultra.jpg" } ], "totalCount": 3 } // Utilisation dans les tests: cypress/e2e/produits.cy.js describe('Catalogue de produits', () => { beforeEach(() => { // Intercepter la requête API et la remplacer par notre fixture cy.intercept('GET', '/api/products*', { fixture: 'products.json' }).as('getProducts'); cy.visit('/products'); cy.wait('@getProducts'); }); it('Affiche la liste des produits correctement', () => { // Vérifie que tous les produits de la fixture sont affichés cy.get('.product-card').should('have.length', 3); cy.contains('Smartphone XYZ').should('be.visible'); cy.contains('Casque Audio Premium').should('be.visible'); cy.contains('Tablette Ultra').should('be.visible'); }); it('Affiche correctement les produits en rupture de stock', () => { // Vérifie que les produits en rupture sont marqués comme tels cy.contains('.product-card', 'Tablette Ultra') .should('contain', 'Rupture de stock') .and('have.class', 'out-of-stock'); }); it('Permet de filtrer les produits par catégorie', () => { // Simuler une autre réponse d'API pour le filtre cy.intercept('GET', '/api/products?category=Électronique', { fixture: 'filtered-products.json' }).as('getFilteredProducts'); // Cliquer sur le filtre de catégorie cy.get('select[name="category"]').select('Électronique'); cy.wait('@getFilteredProducts'); // Vérifier que les résultats filtrés sont bien affichés cy.get('.product-card').should('have.length', 2); cy.contains('Smartphone XYZ').should('be.visible'); cy.contains('Tablette Ultra').should('be.visible'); cy.contains('Casque Audio Premium').should('not.exist'); }); });

Tests de composants

Depuis la version 10, Cypress permet également de tester des composants isolés React, Vue ou Angular :

Test de composant React avec Cypress
// Exemple de test de composant React avec Cypress // cypress/component/Button.cy.jsx import Button from '../../src/components/Button'; describe('Button Component', () => { it('Affiche le texte du bouton correctement', () => { // Monte le composant cy.mount(<Button>Cliquez-moi</Button>); // Vérifie que le texte est affiché cy.get('button').should('have.text', 'Cliquez-moi'); }); it('Appelle le callback onClick quand on clique sur le bouton', () => { // Créer un spy pour le onClick const onClickSpy = cy.spy().as('onClickSpy'); // Monte le composant avec le spy cy.mount(<Button onClick={onClickSpy}>Cliquez-moi</Button>); // Clique sur le bouton cy.get('button').click(); // Vérifie que le spy a été appelé cy.get('@onClickSpy').should('have.been.calledOnce'); }); it('Applique la classe "disabled" quand la prop disabled est true', () => { // Monte le composant avec prop disabled cy.mount(<Button disabled>Désactivé</Button>); // Vérifie que la classe est appliquée cy.get('button') .should('have.class', 'disabled') .and('have.attr', 'disabled'); }); it('Applique les styles variant correctement', () => { // Monte le composant avec variant primary cy.mount(<Button variant="primary">Primaire</Button>); // Vérifie la classe de style cy.get('button').should('have.class', 'btn-primary'); // Monte le composant avec variant secondary cy.mount(<Button variant="secondary">Secondaire</Button>); // Vérifie la classe de style cy.get('button').should('have.class', 'btn-secondary'); }); });

Configuration et bonnes pratiques

La configuration de Cypress se fait principalement via le fichier cypress.config.js (ou cypress.config.ts pour Icône TypeScriptTypeScript) :

cypress.config.js
const { defineConfig } = require('cypress') module.exports = defineConfig({ e2e: { baseUrl: 'http://localhost:3000', viewportWidth: 1280, viewportHeight: 720, defaultCommandTimeout: 5000, screenshotOnFailure: true, video: true, retries: { runMode: 2, openMode: 0 }, setupNodeEvents(on, config) { // plugins et hooks peuvent être configurés ici }, }, component: { devServer: { framework: 'react', bundler: 'webpack', }, }, })

Voici quelques bonnes pratiques pour utiliser Cypress efficacement :

  • Tests isolés - Chaque test doit pouvoir s'exécuter indépendamment des autres
  • Données de test propres - Utilisez cy.intercept() et des fixtures pour contrôler l'état de votre application
  • Sélecteurs robustes - Utilisez des attributs data-cy spécifiques plutôt que des classes CSS ou des sélecteurs complexes
  • Commandes personnalisées - Factoriser les opérations répétitives dans des commandes personnalisées
  • Ne pas tester des aspects visuels précis - Cypress n'est pas destiné aux tests de pixel-perfect, mais plutôt aux comportements fonctionnels
  • Utiliser cy.session() - Pour préserver l'état de session entre les tests et éviter de se reconnecter à chaque fois
  • Ne pas abuser de cy.wait() - Préférer les attentes implicites comme .should('be.visible')

Avantages et limitations

Avantages de Cypress

  • Expérience développeur exceptionnelle - Interface visuelle, debugging en temps réel, time travel
  • Tests plus fiables - Moins de tests flaky grâce aux attentes automatiques
  • Vitesse d'exécution - Plus rapide que la plupart des frameworks E2E traditionnels
  • Documentation excellente - Une des meilleures documentations dans l'écosystème des outils de test
  • Écosystème riche - Nombreux plugins et intégrations disponibles

Limitations

  • Support limité des navigateurs - Bien que amélioré récemment, ne supporte pas tous les navigateurs comme Safari
  • Pas d'exécution cross-domain - Les tests sont limités à un seul domaine par test
  • Pas de support natif pour plusieurs onglets - Les tests sont limités à un seul onglet
  • Coût pour certaines fonctionnalités avancées - Cypress Dashboard payant pour les équipes au-delà d'un certain seuil

Cypress est un outil puissant qui a révolutionné la façon dont les développeurs créent et maintiennent des tests end-to-end. Sa facilité d'utilisation, son interface visuelle et sa capacité à créer des tests fiables en font un choix privilégié pour de nombreuses équipes de développement web modernes.

Applications concrètes

Cas d'usage

Applications React/NextJS

Tests end-to-end complets pour les applications Icône ReactReact et Icône Next.jsNext.js, couvrant les parcours utilisateurs critiques, le rendering des composants, et la gestion d'état globale.

Sites e-commerce

Vérification automatisée des parcours d'achat complets, depuis la navigation dans le catalogue jusqu'à la finalisation de la commande, en passant par les interactions avec le panier.

Tests de validation de formulaires

Vérification exhaustive du comportement des formulaires complexes, incluant la validation des champs, la gestion des erreurs et les soumissions réussies.

Applications SaaS

Tests des fonctionnalités critiques des applications SaaS, comme l'onboarding, la gestion des abonnements, les tableaux de bord et les fonctionnalités spécifiques aux différents plans.

Applications à forte interaction

Test des interfaces utilisateurs complexes comme les éditeurs de texte riches, les applications de dessin, les tableaux de données interactifs ou les dashboards avec drag-and-drop.

Test de composants isolés

Avec Component Testing, test unitaire de composants Icône ReactReact, Icône VueVue ou Angular de manière isolée, permettant de vérifier leur comportement et leur rendu dans différents états.

Intégration dans les pipelines CI/CD

Icône CypressCypress s'intègre parfaitement dans les workflows d'intégration continue pour automatiser les tests à chaque commit ou pull request. Voici un exemple de configuration pour Icône GitHub ActionsGitHub Actions :

name: E2E Tests on: push: branches: [ main, develop ] pull_request: branches: [ main, develop ] jobs: cypress-run: runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v3 - name: Set up Node.js uses: actions/setup-node@v3 with: node-version: 16 cache: 'npm' - name: Install dependencies run: npm ci - name: Build application run: npm run build - name: Start server in background run: npm run start & npx wait-on http://localhost:3000 - name: Run Cypress tests uses: cypress-io/github-action@v5 with: browser: chrome record: true env: CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Upload screenshots uses: actions/upload-artifact@v3 if: failure() with: name: cypress-screenshots path: cypress/screenshots - name: Upload videos uses: actions/upload-artifact@v3 if: always() with: name: cypress-videos path: cypress/videos