← Back to React Course | Chapter 12: Testing React Applications | Lesson 9 of 9

End-to-End Testing with Cypress

End-to-end testing with Cypress is like hiring a mystery shopper to walk through your entire store from the front door to the checkout counter, checking that the whole experience actually works.

What Makes a Test 'End-to-End'

Unlike unit or integration tests that check isolated pieces, an end-to-end (E2E) test drives your ACTUAL running app in a real browser, simulating a complete user journey from start to finish — like visiting a signup page, filling the form, submitting, and confirming a welcome message appears.

Note: E2E tests give the highest confidence that a real feature works, since they exercise the whole stack, but they're also the slowest kind of test to run.

Warning: Because E2E tests are slow and can be flaky, reserve them for your most critical flows (signup, checkout, login) rather than every small feature.

Example: What Makes a Test 'End-to-End'

markup
// Run in your local React project (npm install required)
// cypress/e2e/login.cy.js
describe('Login flow', () => {
  it('logs in successfully', () => {
    cy.visit('/login');
    cy.get('input[name=email]').type('[email protected]');
    cy.get('button').contains('Log In').click();
    cy.contains('Welcome back!').should('be.visible');
  });
});

Core Cypress Commands

cy.visit(url) navigates to a page, cy.get(selector) finds elements (similar in spirit to Testing Library's queries, but CSS-selector-based), and commands like .click() or .type() interact with them, all chained together to describe a full user flow.

Note: Cypress commands are chainable, so you can write a fluent sequence like cy.get(input).type(hello).should('have.value', hello).

Warning: Cypress commands are asynchronous under the hood even though they don't need await in your code — Cypress manages the queueing and retrying automatically.

Example: Core Cypress Commands

markup
// Run in your local React project (npm install required)
describe('Search feature', () => {
  it('shows results after searching', () => {
    cy.visit('/search');
    cy.get('input[type=search]').type('react{enter}');
    cy.get('.result-item').should('have.length.greaterThan', 0);
  });
});

Automatic Retrying and Avoiding Flaky Tests

Cypress automatically retries most assertions for a few seconds before failing, which handles a lot of async timing (like waiting for an API response to render) without manual sleeps or arbitrary waits. This built-in retrying reduces, though doesn't fully eliminate, timing-related test flakiness.

Note: Avoid cy.wait(1000)-style fixed delays — let Cypress's built-in retrying handle timing, asserting on the eventual state instead.

Warning: Relying on fixed-duration waits instead of Cypress's retry-based assertions makes tests slower on fast runs and still flaky on genuinely slow ones.

Example: Automatic Retrying and Avoiding Flaky Tests

markup
// Run in your local React project (npm install required)
// Avoid this (fixed, unreliable wait):
// cy.wait(2000);
// cy.get('.result').should('exist');

// Prefer this (Cypress retries automatically until it appears or times out):
cy.get('.result', { timeout: 10000 }).should('exist');
Common Mistakes
  1. Writing E2E tests for every small detail instead of reserving them for critical, full user journeys.
  2. Making E2E tests depend on real, external, unpredictable data instead of a controlled test environment.
  3. Not waiting properly for async content, causing tests to fail intermittently ('flaky tests') depending on timing.
Chapter Summary
  • Cypress is a popular end-to-end testing tool that drives a real browser to simulate complete user flows.
  • cy.visit(), cy.get(), and cy.click() are core commands for navigating and interacting with a real page.
  • Cypress automatically retries assertions for a short time, reducing (but not eliminating) timing-related flakiness.
  • E2E tests are slower than unit tests, so they're reserved for critical, high-value user flows.
Browser Support

Requires npm install cypress — runs a real browser via Cypress's test runner, not this preview sandbox.

Login to run this code

C/C++/Java/PHP execution requires a free account. Your code is saved — you'll land right back in the editor after logging in.