Skip to content

Cypress Selectors

Choosing the right selector is one of the highest-leverage decisions in a Cypress test. This lesson explains the selector types Cypress supports and why dedicated test attributes outperform CSS class selectors.

What Is a Selector in Cypress?

A selector is the string you pass to cy.get() to locate one or more elements, using the same syntax as jQuery/CSS selectors: tag names, classes, IDs, attribute selectors, and combinators.

While any valid CSS selector works, not all selectors are equally reliable over time. Selectors tied to styling (like .btn-primary) or generated markup (like auto-generated framework classes) tend to break when a designer or developer changes unrelated styling.

cy.get('[data-cy="submit-order"]').click();

A dedicated data-cy attribute selector targets exactly one purpose-built hook, immune to styling or markup refactors.

Selector Syntax Cypress Supports

cy.get('button')                 // tag
cy.get('.btn-primary')           // class
cy.get('#submit')                // id
cy.get('[data-cy="submit"]')     // attribute
cy.get('ul > li')                // combinator
  • Any selector jQuery/CSS understands is valid inside cy.get().
  • Attribute selectors like [data-cy="x"] are the recommended approach for test-owned hooks.
  • Combinators (>, , +, ~) work exactly as they do in CSS.
  • cy.contains() is a separate command for text-based matching, covered in the next lesson.

Selector Type Cheat Sheet

Common selector patterns ranked roughly from least to most resilient.

Selector Type Example Stability
Tag name cy.get('button') Low, matches too broadly
CSS class cy.get('.btn-primary') Low, tied to styling
Auto-generated class cy.get('.css-1x2y3z') Very low, changes on rebuild
ID cy.get('#checkout') Medium, but often reused for styling/JS too
ARIA role/label cy.get('[role="button"]') Good, and improves accessibility coverage
data-testid cy.get('[data-testid="x"]') High, purpose-built for tests
data-cy cy.get('[data-cy="x"]') Highest, dedicated Cypress convention

Why data-cy Attributes Are Recommended

A data-cy (or data-testid) attribute exists solely to be targeted by tests. Because it carries no styling or JavaScript behavior, changing a class name, refactoring a component's markup, or switching CSS frameworks does not affect it.

This decouples your test suite from implementation details that change frequently for reasons unrelated to functional correctness, dramatically reducing false-positive test failures.

<button class="btn btn-primary rounded-lg" data-cy="submit-order">
  Place Order
</button>

The class attribute can change freely for styling reasons without ever breaking cy.get('[data-cy="submit-order"]').

Adding data-cy Attributes to an Existing Codebase

Retrofitting data-cy attributes into an existing application does not require touching every element, only the ones your tests actually need to interact with or assert on: buttons, form fields, key status messages, and navigation links.

  • Add attributes incrementally, starting with the highest-value flows (login, checkout, core navigation).
  • Use a consistent naming convention, like feature-element, e.g. checkout-submit.
  • Treat data-cy attributes as part of your component's public contract, not an implementation detail.

Common Mistakes

  • Selecting elements by auto-generated CSS-in-JS class names that change on every build.
  • Reusing an element's id for both styling/JavaScript hooks and test selection, creating coupling in both directions.
  • Writing deep, fragile selector chains like .container > div:nth-child(3) > span instead of a direct attribute selector.
  • Adding data-cy attributes only after tests start failing, instead of as part of building the component.

Key Takeaways

  • Cypress accepts any valid CSS/jQuery selector inside cy.get().
  • Selectors tied to styling or auto-generated markup are the least stable choice over time.
  • Dedicated data-cy (or data-testid) attributes are the most resilient, purpose-built option.
  • Add test attributes incrementally, prioritizing the highest-value user flows first.

Pro Tip

Treat adding a data-cy attribute as part of "done" when building a new interactive component, it costs seconds during development but saves significant debugging time when a future refactor would otherwise silently break a test.