Skip to main content

Cypress Testing Guide

The complete reference for Cypress — the developer-first frontend testing framework. Covers E2E tests, component testing, network interception, custom commands, and CI integration.

✓ Time-travel debugging ✓ Component testing ✓ Network stubbing ✓ Real browser execution ✓ Auto-retry assertions

What is Cypress?

Cypress is a JavaScript testing framework built specifically for the modern web. Unlike Selenium-era tools that drive browsers through WebDriver, Cypress runs directly inside the browser alongside your application. This architecture enables unique features: time-travel debugging (hover over any command in the runner to see the DOM state at that moment), real-time test reloading, direct access to application code and network requests, and automatic retries on assertions.

Cypress unified E2E and component testing under one API. With Component Testing mode, you can mount a React or Vue component directly in Cypress without a full app running, test it with the same cy.get() and cy.contains() commands, and get screenshots and video. This makes Cypress the only tool that covers both granular component tests and full user journey tests in a single runner.

Installation

# Install Cypress
npm install --save-dev cypress

# Open interactive runner
npx cypress open

# Run headlessly (CI)
npx cypress run

# cypress.config.ts
import { defineConfig } from 'cypress';

export default defineConfig({
  e2e: {
    baseUrl: 'http://localhost:3000',
    specPattern: 'cypress/e2e/**/*.cy.ts',
    supportFile: 'cypress/support/e2e.ts',
    video: false,               // Enable for CI
    screenshotOnRunFailure: true,
  },
  component: {
    devServer: { framework: 'react', bundler: 'vite' },
  },
});

Basic E2E Test: cy.visit, cy.get, cy.contains

// cypress/e2e/login.cy.ts
describe('Login flow', () => {
  beforeEach(() => {
    cy.visit('/login');
  });

  it('logs in with valid credentials', () => {
    cy.get('[data-testid="email"]').type('[email protected]');
    cy.get('[data-testid="password"]').type('secret{enter}');

    cy.url().should('include', '/dashboard');
    cy.contains('h1', 'Dashboard').should('be.visible');
  });

  it('shows error for wrong password', () => {
    cy.get('[data-testid="email"]').type('[email protected]');
    cy.get('[data-testid="password"]').type('wrong');
    cy.get('button[type="submit"]').click();

    cy.get('[role="alert"]').should('contain', 'Invalid credentials');
  });
});

Network Stubbing with cy.intercept()

describe('Products page', () => {
  it('shows products from API', () => {
    // Stub the API response
    cy.intercept('GET', '/api/products', {
      statusCode: 200,
      body: [
        { id: 1, name: 'Widget', price: 9.99 },
        { id: 2, name: 'Gadget', price: 19.99 },
      ],
    }).as('getProducts');

    cy.visit('/products');
    cy.wait('@getProducts');

    cy.contains('Widget').should('be.visible');
    cy.contains('Gadget').should('be.visible');
  });

  it('shows empty state when API returns empty array', () => {
    cy.intercept('GET', '/api/products', { body: [] }).as('getProducts');
    cy.visit('/products');
    cy.wait('@getProducts');
    cy.contains('No products found').should('be.visible');
  });

  it('handles API error', () => {
    cy.intercept('GET', '/api/products', { statusCode: 500 }).as('getProducts');
    cy.visit('/products');
    cy.wait('@getProducts');
    cy.contains('Something went wrong').should('be.visible');
  });
});

Custom Commands

// cypress/support/commands.ts
Cypress.Commands.add('login', (email: string, password: string) => {
  // Programmatic login via API — much faster than UI login
  cy.request('POST', '/api/auth/login', { email, password })
    .then(({ body }) => {
      window.localStorage.setItem('token', body.token);
    });
});

// Use cy.session() to cache login across tests
Cypress.Commands.add('loginAs', (role: 'admin' | 'user') => {
  cy.session(role, () => {
    const creds = { admin: ['[email protected]', 'admin'], user: ['[email protected]', 'user'] };
    cy.request('POST', '/api/auth/login', {
      email: creds[role][0], password: creds[role][1],
    }).then(({ body }) => {
      window.localStorage.setItem('token', body.token);
    });
  });
});

// In tests:
// cy.login('[email protected]', 'password');
// cy.loginAs('admin');

Component Testing

// cypress/component/Button.cy.tsx
import Button from '../../src/components/Button';

describe('Button component', () => {
  it('renders with correct label', () => {
    cy.mount(<Button label="Click me" onClick={cy.stub()} />);
    cy.contains('Click me').should('be.visible');
  });

  it('calls onClick when clicked', () => {
    const onClick = cy.stub().as('onClick');
    cy.mount(<Button label="Submit" onClick={onClick} />);
    cy.get('button').click();
    cy.get('@onClick').should('have.been.calledOnce');
  });

  it('is disabled when disabled prop is true', () => {
    cy.mount(<Button label="Disabled" onClick={cy.stub()} disabled />);
    cy.get('button').should('be.disabled');
  });
});

Key Features

Comparison: Cypress vs Alternatives

Feature Cypress Playwright Selenium
Architecture In-browser External protocol WebDriver
Browser support Chrome/FF/Edge Chrome/FF/Safari All browsers
Component testing Yes No No
Time-travel debug Yes Trace Viewer No
Multi-tab support No Yes Yes
Developer experience Excellent Excellent Moderate

Pros & Cons

Pros

  • Best-in-class developer experience and interactive runner
  • Time-travel debugging eliminates most debugging effort
  • Component testing covers UI unit tests too
  • cy.intercept() makes network stubbing effortless
  • Runs in a real browser, not a simulation
  • Excellent documentation and community

Cons

  • No Safari/WebKit support (no real iOS browser testing)
  • Cannot open multiple tabs in a single test
  • JavaScript/TypeScript only (no Python, Java, etc.)
  • Can be slow for large suites without Cloud parallelization
  • Same-origin restrictions on cross-domain navigation
  • Cypress Cloud required for CI parallelization

Frequently Asked Questions

What is Cypress and what makes it different from other E2E testing tools?▼

Cypress is a JavaScript end-to-end testing framework that runs directly inside the browser rather than controlling it from outside (as Selenium and Playwright do). This architecture gives Cypress native access to the DOM, network layer, and application code. Its standout features are time-travel debugging (see the DOM state at every command), automatic retries, real-time reloading, and an interactive test runner with screenshot and video recording built in.

What is the difference between Cypress E2E testing and Cypress Component Testing?▼

Cypress E2E tests navigate to a running web server URL and test the full application stack. Cypress Component Testing mounts individual components in isolation inside a real browser without a dev server, making it faster than E2E for UI-level unit tests. Component Testing supports React, Vue, Angular, Svelte, and Next.js, and shares the same cy.get/cy.contains API — making it a natural companion to E2E tests in a single tool.

How does cy.intercept() work?▼

cy.intercept() registers a network handler that intercepts HTTP requests matching a URL pattern or method before they leave the browser. You can let the real request through, stub it with a fixture, modify request/response headers, or simulate network failures. Unlike Playwright's page.route(), Cypress intercepts happen at the XHR/Fetch layer rather than the OS network layer, meaning the app's JavaScript sees the stubbed response as if it came from the real server.

How do I write reusable Cypress custom commands?▼

Add custom commands to cypress/support/commands.ts using Cypress.Commands.add(). A common pattern is a cy.login() command that programmatically sets a session token without going through the UI — dramatically faster than filling the login form on every test. Declare the TypeScript type in cypress/support/index.d.ts to get autocomplete. Use cy.session() to cache and restore session state across tests for even faster authentication.

Why do Cypress tests sometimes fail intermittently (flakiness)?▼

Cypress retries most assertions automatically for up to 4 seconds, but flakiness can occur when: (1) an assertion checks a value that updates after an async operation not tracked by Cypress; (2) tests share state via the DOM or application store between runs; (3) cy.wait() is used with a hard-coded duration instead of waiting for a specific network alias. Fix flakiness by using cy.intercept() aliases and cy.wait('@alias'), avoiding fixed waits, and clearing application state in beforeEach.

What is Cypress Cloud (formerly Cypress Dashboard)?▼

Cypress Cloud is the commercial SaaS platform for storing test recordings, screenshots, and videos from CI runs. It provides parallelization (distribute tests across multiple CI machines automatically), flake detection, test analytics, and GitHub/GitLab integrations for PR status checks. The Cypress open-source runner works without Cypress Cloud, but Cloud adds observability and parallelization for large test suites.

Share this tool: X / Twitter LinkedIn
✓ 60,000+ developers use DevPlaybook ★★★★★ 4.8 / 5 on Gumroad ✓ MIT licensed · Instant download ✓ No login required

Related Products

on Gumroad

You Might Also Need

More Free Tools

Enjoying this tool? Get more like it.

Weekly developer tool guides, productivity playbooks & AI tricks. Free. No spam.

DevPlaybook Pro 7 days free

Unlock AI-Powered Dev Tools

  • ⚡ AI Code Review, Doc Generator & SQL Builder
  • ⚡ All premium templates & early access
  • ⚡ Member discounts on Gumroad products
Start Free Trial →

$9/mo after trial · Cancel anytime

Want the full toolkit?

Get DevPlaybook Pro

Every template, guide, boilerplate, and automation script in one bundle. 13 premium products — grab them all at once and save big.

$79
$241 separately Save $162 (67% off)
Get DevPlaybook Pro — $79 →

MIT licensed · Instant download · No subscription

See what's included → Browse all deals →