# Cypress Best Practices for Test Writing: The Official Style Guide

> Learn Cypress best practices for writing maintainable tests. Use accessibility-first selectors, stub network calls, and i18n for reliable test suites.

- Repository: [Cypress.io/cypress](https://github.com/cypress-io/cypress)
- Tags: best-practices
- Published: 2026-06-18

---

**Write Cypress tests using accessibility-first selectors, stub network calls with `cy.intercept`, and centralize UI strings via i18n files to create maintainable, deterministic test suites.**

The cypress-io/cypress repository maintains rigorous standards for test writing in its official **Testing Strategy and Style Guide**. Following these Cypress best practices for test writing ensures your end-to-end, component, and integration tests remain readable, accessible, and resilient against UI changes.

## Adopt a Test-Driven Workflow

The Cypress team recommends a strict test-driven development approach. Write a failing test before implementing the feature, then iterate until the test passes. According to the [`guides/testing-strategy-and-styleguide.md`](https://github.com/cypress-io/cypress/blob/main/guides/testing-strategy-and-styleguide.md), this applies across all test types: **unit tests** for isolated functions, **integration tests** for feature-level logic with mocked dependencies, **component tests** for UI pieces, and **end-to-end (E2E) tests** for full-stack flows.

## Choose the Appropriate Test Type

Selecting the correct test type maximizes confidence while minimizing execution time.

- **Unit tests** validate isolated functions and business logic without UI dependencies.
- **Integration tests** verify feature-level behavior using mocked external services.
- **Component tests**, utilizing helpers from [`packages/app/src/mountFragment.ts`](https://github.com/cypress-io/cypress/blob/main/packages/app/src/mountFragment.ts), mount individual UI fragments and mock GraphQL data.
- **E2E tests** exercise complete user flows through the entire application stack.

## Prioritize Accessibility-First Selectors

Avoid fragile CSS selectors that break when styling changes. Instead, locate elements by their accessible names as implemented in the Cypress testing strategy.

Use `cy.contains()` with specific element types or `findByLabelText` for form fields:

```javascript
// ✅ Prefer an accessible selector
cy.contains('button', 'Log In').click()

// ✅ Use a label-based selector for a form field
cy.findByLabelText('Email address').type('user@example.com')

```

When accessibility attributes cannot expressionally target an element, augment with `data-cy` attributes. This pattern appears throughout the style guide for non-interactive elements:

```javascript
// ✅ Combine data-cy with a label for a non-interactive element
cy.get('[data-cy="success-toast"]')
  .should('be.visible')
  .and('contain.text', 'Operation successful')

```

## Centralize Reusable Strings

Hard-coding UI text in tests creates maintenance burdens during localization. The Cypress style guide recommends pulling interface text into i18n JSON files (such as [`en-US.json`](https://github.com/cypress-io/cypress/blob/main/en-US.json)) and importing those constants in both the application and test suites. This ensures consistency across the codebase and simplifies updates when text changes.

## Optimize Assertions and Visibility Checks

Keep assertions close to their target elements to minimize brittleness. The [`guides/testing-strategy-and-styleguide.md`](https://github.com/cypress-io/cypress/blob/main/guides/testing-strategy-and-styleguide.md) specifically warns against deep DOM traversals that couple tests to implementation details.

Verify visibility only when the element is not being interacted with. Implicit assertions during interactions (like `.click()` or `.type()`) automatically check for visibility and enabled state, making explicit visibility checks redundant in those contexts.

## Leverage Cypress Testing Library Judiciously

While Cypress Testing Library provides semantic queries like `findByRole` and `findByLabelText`, the official guide recommends using `cy.contains` for most cases. Reserve `findByRole` and `findByLabelText` for scenarios where they provide clear accessibility value, keeping tests concise and readable.

## Handle Network Requests and Eliminate Flakiness

Guard against flaky tests by using deterministic selectors and avoiding timing-related hacks. Never use arbitrary `cy.wait()` calls with millisecond values.

Instead, stub network requests using `cy.intercept`, implemented in [`packages/driver/src/cy/intercept.ts`](https://github.com/cypress-io/cypress/blob/main/packages/driver/src/cy/intercept.ts), to control external dependencies:

```javascript
// ✅ Stub a network request in an E2E test
cy.intercept('GET', '/api/users', { fixture: 'users.json' }).as('getUsers')
cy.visit('/users')
cy.wait('@getUsers')
cy.get('[data-cy="user-list"]').should('contain', 'John Doe')

```

This pattern ensures tests wait for specific network conditions rather than arbitrary timeouts, creating reliable, deterministic test execution.

## Implement Visual Regression Testing

Avoid asserting against hard-coded CSS values, which break when design systems evolve. The Cypress testing strategy recommends using **Percy** or similar visual snapshot tools to detect unintended UI changes. This approach validates visual appearance without coupling tests to specific style implementations.

## Summary

- **Write tests first** using a test-driven workflow across unit, integration, component, and E2E layers.
- **Select elements** by accessible names using `cy.contains` or `findByLabelText`, falling back to `data-cy` attributes when necessary.
- **Centralize strings** in i18n files to maintain consistency between application and test code.
- **Stub network calls** with `cy.intercept` to eliminate flakiness and external dependencies.
- **Use visual snapshots** rather than CSS assertions to validate UI appearance.

## Frequently Asked Questions

### How do I select elements in Cypress without creating brittle tests?

Prefer **accessibility-first selectors** such as `cy.contains('button', 'Submit')` or `cy.findByLabelText('Email')` over CSS classes or IDs. When accessibility markup is insufficient, use `data-cy` attributes, which remain stable during styling changes. This approach, documented in [`guides/testing-strategy-and-styleguide.md`](https://github.com/cypress-io/cypress/blob/main/guides/testing-strategy-and-styleguide.md), decouples your tests from implementation details.

### What is the difference between component tests and E2E tests in Cypress?

**Component tests** mount individual UI fragments in isolation using utilities like [`packages/app/src/mountFragment.ts`](https://github.com/cypress-io/cypress/blob/main/packages/app/src/mountFragment.ts), mocking GraphQL data and dependencies. **E2E tests** navigate the actual application URL and exercise the full stack including real routing and backend integration. Use component tests for rapid UI feedback and E2E tests for critical user flows.

### How should I handle waiting for network requests in Cypress?

Never use arbitrary `cy.wait()` with millisecond values. Instead, use **`cy.intercept()`** to stub the route and assign an alias, then wait for that specific alias using `cy.wait('@alias')`. This pattern, implemented in [`packages/driver/src/cy/intercept.ts`](https://github.com/cypress-io/cypress/blob/main/packages/driver/src/cy/intercept.ts), ensures tests wait precisely for the expected network call rather than timing delays.

### Where should I store reusable test strings in a Cypress test suite?

Store UI text in your application's **i18n JSON files** (such as [`en-US.json`](https://github.com/cypress-io/cypress/blob/main/en-US.json)) and import those constants into your test files. This practice, recommended in the testing style guide, ensures that when application text changes, tests automatically reference the updated values without manual synchronization.