What makes Cypress differentCe qui rend Cypress différent
Most E2E tools drive a browser from the outside — they send commands over a protocol and wait for responses. Cypress runs inside the browser, in the same execution context as the application. This architectural choice is what makes it fast, reliable, and debuggable in a way that Selenium-based tools simply aren't. La plupart des outils E2E pilotent un navigateur depuis l'extérieur — ils envoient des commandes via un protocole et attendent des réponses. Cypress tourne à l'intérieur du navigateur, dans le même contexte d'exécution que l'application. Ce choix architectural le rend rapide, fiable et débogable d'une façon que les outils basés sur Selenium ne permettent tout simplement pas.
The result: no more sleep() calls,
no more flaky timeouts, and a time-travel debugger that shows a snapshot of the DOM at every command.
Le résultat : plus d'appels sleep(),
plus de timeouts instables, et un débogueur time-travel qui montre un snapshot du DOM à chaque commande.
Project structureStructure du projet
Cypress has an opinionated folder structure. Respecting it keeps tests organized and makes onboarding new team members straightforward. Cypress a une structure de dossiers imposée. La respecter garde les tests organisés et facilite l'intégration de nouveaux membres dans l'équipe.
cypress/ ├── e2e/ │ ├── login.cy.js │ └── dashboard.cy.js ├── fixtures/ │ └── users.json // test data ├── support/ │ ├── commands.js // custom cy.* commands │ └── e2e.js // global hooks / imports └── cypress.config.js // baseUrl, timeouts, env vars
Writing a real testÉcrire un vrai test
Cypress tests read like plain English. Commands are chained, assertions are built in, and the automatic waiting means you never have to think about timing. Les tests Cypress se lisent comme de l'anglais courant. Les commandes se chaînent, les assertions sont intégrées, et l'attente automatique signifie que tu n'as jamais à penser au timing.
// Load test data from fixture import users from '../fixtures/users.json'; describe('Login page', () => { beforeEach(() => { cy.visit('/login'); // baseUrl from config }); it('logs in with valid credentials', () => { cy.get('#username').type(users.valid.email); cy.get('#password').type(users.valid.password); cy.get('[data-cy="submit"]').click(); cy.url().should('include', '/dashboard'); cy.get('[data-cy="welcome"]').should('be.visible'); }); it('shows error on wrong password', () => { cy.get('#username').type(users.valid.email); cy.get('#password').type('wrong-password'); cy.get('[data-cy="submit"]').click(); cy.get('[data-cy="error"]') .should('be.visible') .and('contain', 'Invalid credentials'); }); });
💡 Best practice : always use data-cy
attributes as selectors. They are immune to CSS changes, class renames, and refactoring —
the test never breaks because a designer changed a class name.
💡 Bonne pratique : toujours utiliser les attributs
data-cy comme sélecteurs. Ils sont immunisés
contre les changements CSS, les renommages de classes et les refactorisations —
le test ne casse jamais parce qu'un designer a changé un nom de classe.
Network interception with cy.interceptInterception réseau avec cy.intercept
One of Cypress's most powerful features is the ability to intercept, inspect, and stub HTTP requests — without a proxy, without a separate mock server, just one line of code. L'une des fonctionnalités les plus puissantes de Cypress est la capacité d'intercepter, inspecter et stubber les requêtes HTTP — sans proxy, sans serveur mock séparé, juste une ligne de code.
it('loads user list from API', () => { // Intercept the real API call cy.intercept('GET', '/api/users').as('getUsers'); cy.visit('/users'); cy.wait('@getUsers').its('response.statusCode').should('eq', 200); cy.get('[data-cy="user-row"]').should('have.length.gt', 0); }); it('shows empty state when API returns no users', () => { // Stub the response to control the test scenario cy.intercept('GET', '/api/users', { body: [] }).as('emptyUsers'); cy.visit('/users'); cy.wait('@emptyUsers'); cy.get('[data-cy="empty-state"]').should('be.visible'); });
Custom commandsCommandes personnalisées
Repetitive sequences — like logging in before every test — should become custom commands.
They live in support/commands.js and are
available globally as cy.*.
Les séquences répétitives — comme se connecter avant chaque test — doivent devenir des
commandes personnalisées. Elles vivent dans support/commands.js
et sont disponibles globalement en tant que cy.*.
// Reusable login via API — bypasses the UI, much faster Cypress.Commands.add('loginByApi', (email, password) => { cy.request({ method: 'POST', url: '/api/auth/login', body: { email, password }, }).then(({ body }) => { window.localStorage.setItem('token', body.token); }); }); // Usage in any test: // cy.loginByApi('user@example.com', 'pass123');
Cypress vs Selenium — honest comparisonCypress vs Selenium — comparaison honnête
✓ Cypress strengths✓ Points forts Cypress
- ⚡ Automatic waiting — no flaky timeoutsAttente automatique — pas de timeout instable
- 🔍 Time-travel debuggerDébogueur time-travel
- 🌐 Built-in network interceptionInterception réseau intégrée
- 📸 Auto screenshots & videos on failureScreenshots et vidéos auto en échec
- 🚀 Fast setup, zero config to startSetup rapide, zéro config pour démarrer
✗ Known limitations✗ Limitations connues
- 🪟 No multi-tab / multi-window supportPas de multi-onglets / multi-fenêtres
- 🌍 Limited cross-browser (mainly Chrome-based)Cross-browser limité (surtout Chrome)
- 📱 No native mobile app testingPas de test d'app mobile native
- 🖥️ Web only — no desktop appsWeb uniquement — pas d'apps desktop
CI integration with GitHub ActionsIntégration CI avec GitHub Actions
name: Cypress E2E on: [push, pull_request] jobs: e2e: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: cypress-io/github-action@v6 with: build: npm run build start: npm start wait-on: 'http://localhost:3000' env: CYPRESS_BASE_URL: ${{ secrets.BASE_URL }}
The official cypress-io/github-action
handles caching, browser installation, and artifact upload automatically.
Failed test videos and screenshots are available directly in the Actions summary.
L'action officielle cypress-io/github-action
gère le caching, l'installation du navigateur et l'upload des artefacts automatiquement.
Les vidéos et screenshots des tests en échec sont disponibles directement dans le résumé Actions.
Key takeawaysCe qu'il faut retenir
-
Never use cy.wait(ms)Ne jamais utiliser cy.wait(ms)
Always wait on an alias (cy.wait('@alias')) or an assertion. Fixed waits are the root cause of 90% of flaky Cypress tests. Toujours attendre sur un alias (cy.wait('@alias')) ou une assertion. Les attentes fixes sont la cause de 90% des tests Cypress instables. -
data-cy selectors are mandatoryLes sélecteurs data-cy sont obligatoires
Agree on this convention with your dev team from day one. Retrofitting is painful. Se mettre d'accord sur cette convention avec l'équipe dev dès le premier jour. Le rétrofit est douloureux. -
Login via API, not UILogin via API, pas via l'UI
UI login in abeforeEachis slow. A direct API call that sets the session token is 10× faster. Le login UI dans unbeforeEachest lent. Un appel API direct qui pose le token de session est 10× plus rapide. -
Stub external APIsStubber les APIs externes
Third-party services are outside your control. Intercept and stub them so your tests never depend on an external service being up. Les services tiers sont hors de ton contrôle. Les intercepter et les stubber pour que tes tests ne dépendent jamais de la disponibilité d'un service externe.