skip to content

In a Cypress spec, what does `@testing-library/cypress` add to `cy`?

level: middleimportance: should knowfreq 58%

answer

  1. An add-on, not built into Cypress
  2. Imported once in the support file
  3. Only the asynchronous find variants appear
  4. Works as a parent and a chained command
  5. Role queries take a name option

basics

~20 s

It registers Testing Library's asynchronous find queries as Cypress commands - cy.findByRole, cy.findByLabelText, cy.findByTestId and their All variants - so a spec can locate elements by role and accessible name. It is a third-party add-on, imported once in the support file.

solid answer

~40 s

`@testing-library/cypress` is a third-party add-on, not part of Cypress. You import its `@testing-library/cypress/add-commands` entry point once in the Cypress support file, and it registers the asynchronous half of Testing Library's query family — `findByRole`, `findAllByRole`, `findByLabelText`, `findByText`, `findByTestId` and friends — as commands on `cy`. Only the `find*` variants are registered; Testing Library's synchronous `getBy*` and `queryBy*` have no useful shape inside a queue-based runner. Each one works both as a parent command, `cy.findByRole('button', { name: /book deluxe king/i })`, and chained off a subject, `cy.get('[data-cy="guest-form"]').findByLabelText('Guest name')`. They accept a Cypress `timeout` option and keep retrying while the page settles, as in `cy.findByLabelText('Check-in', { timeout: 7000 })`. Nothing else in the runner changes: `cy.get()` and CSS attribute selectors behave exactly as before.

code

javascript · 12 lines
javascript
import '@testing-library/cypress/add-commands'

it('books a room by accessible name', () => {
  cy.visit('/rooms')
  cy.findByRole('button', { name: /book deluxe king/i }).click()
  cy.findByLabelText('Check-in').type('2026-11-14')

  // Chained off a Cypress subject, so the query is scoped to the form
  cy.get('[data-cy="guest-form"]').findByLabelText('Guest name').type('Ada')

  cy.findByRole('dialog', { timeout: 7000 }).should('be.visible')
})

go deeper

for a junior

Know that role and label queries are not part of Cypress out of the box. If a spec calls cy.findByRole, look in the support file for the @testing-library/cypress import that put it there.

for a middle

Explain the wiring: one import in the support file registers the commands for every spec, and each query works as a parent command and chained off a subject. Be able to name several of them.

for a senior

Be ready to say what the add-on costs - a third-party layer on the critical path of every spec, on its own release cadence - and how you would tell from a failure whether the query or the application's accessible name was wrong.

for a principal

Own the dependency decision. Adding the package puts a third-party query layer into every Cypress spec's support file, and that is a commitment the team renews at each Cypress and Testing Library major.

## What the package is `@testing-library/cypress` is a third-party add-on maintained by the Testing Library project, not part of the Cypress binary. Cypress's own documentation endorses it and points at it from the FAQ and from the Playwright and Selenium migration guides, but you install it yourself and it is your dependency to keep current. What it does is narrow: it registers Testing Library's **asynchronous query family** as Cypress commands on `cy`. Once it is wired up, a spec for a hotel booking flow can say `cy.findByRole('button', { name: /book deluxe king/i })` instead of `cy.get('[data-cy="book-room"]')`, and locate the control by the role and accessible name a user perceives rather than by an attribute the team added. ## Wiring it into a Cypress project 1. Install the package as a dev dependency alongside `cypress`. 2. Import its command-registration entry point, `@testing-library/cypress/add-commands`, **once in the Cypress support file** — `cypress/support/e2e.js` for end-to-end testing, `cypress/support/component.js` for component testing. 3. That import runs before every spec, so the commands are available everywhere without a per-spec import. Putting the import in a single spec works for that spec only, and putting it in `setupNodeEvents` does nothing at all: `setupNodeEvents` runs in the Node process, which has no `cy` object to register commands on. Registration has to happen in the browser, which is exactly what the support file is for. ## What lands on `cy` | Command | Finds an element by | Hotel example | | --- | --- | --- | | `cy.findByRole()` | ARIA role, optionally plus accessible name | `cy.findByRole('button', { name: /book/i })` | | `cy.findByLabelText()` | the label associated with a form control | `cy.findByLabelText('Check-in')` | | `cy.findByText()` | visible text content | `cy.findByText('12 rooms available')` | | `cy.findByPlaceholderText()` | a placeholder attribute | `cy.findByPlaceholderText('Guest name')` | | `cy.findByTestId()` | Testing Library's configured test id attribute | `cy.findByTestId('rate-total')` | | `cy.findAllBy*()` | the same, returning every match | `cy.findAllByRole('listitem')` | Only the `find*` and `findAll*` variants are registered. Testing Library's synchronous `getBy*` and `queryBy*` have no useful shape inside a queue-based runner, where a command is enqueued and resolved by Cypress rather than returning a value to the line that called it — so there is no `cy.getByRole()` after installing the add-on, and expecting one is the common stumble for people arriving from React Testing Library. ## How the queries behave inside a Cypress chain - **They work as parent commands.** `cy.findByRole('dialog')` starts a chain the way `cy.get()` does. - **They also work as child commands.** `cy.get('[data-cy="guest-form"]').findByLabelText('Guest name')` scopes the query to the form's subtree, which is how you disambiguate a label that appears in more than one panel of the booking page. - **They retry and accept Cypress's `timeout` option.** Cypress's own FAQ example is `cy.findByLabelText(/Label text/i, { timeout: 7000 }).should('exist')` — the query keeps looking while the page settles rather than failing on the first miss. - **They take Testing Library's matcher arguments.** A string, a regular expression or a matcher function, plus the query's own options object — `{ name: /book/i }` for a role query, for instance. ## Where they meet a test attribute `cy.findByTestId()` reads whichever attribute Testing Library's own `testIdAttribute` setting names, and that setting defaults to `data-testid`. A project standardised on `data-cy` repoints it with Testing Library's own `configure({ testIdAttribute: 'data-cy' })`, called in the same support file. This is worth keeping straight, because Cypress has a similar-sounding setting that has nothing to do with it. `Cypress.ElementSelector.defaults({ selectorPriority })` orders the attributes Cypress prefers when **Cypress generates a selector for you** in Studio or the Selector Playground. It is not consulted by a third-party query, and moving `data-cy` to the front of it will not make `cy.findByTestId()` read `data-cy`. ## What the add-on does not change Installing it does not remove `cy.get()`, does not change what a CSS attribute selector matches, and does not make Cypress aware of ARIA roles anywhere else in the runner. It adds commands, and everything else behaves as before. The cost is the ordinary cost of a dependency sitting in the support file of every spec: it has its own release cadence, and a Cypress or Testing Library major is a moment to check it still loads before the suite does.

  • In a Cypress spec, why is there no `cy.getByRole()` after installing `@testing-library/cypress`?
    The package registers only Testing Library's asynchronous `find*` and `findAll*` queries. A Cypress command is enqueued and resolved by the runner rather than returning a value to the line that called it, so a synchronous `getBy*` has nothing sensible to hand back inside a spec. `cy.findByRole()` is the Cypress-shaped equivalent, and it retries while the page settles instead of failing on the first look.
  • Can `cy.findByTestId()` be pointed at a `data-cy` attribute in a Cypress spec?
    Yes, but through Testing Library's own configuration rather than Cypress's. The query reads the attribute named by Testing Library's `testIdAttribute` setting, which defaults to `data-testid`; calling its `configure({ testIdAttribute: 'data-cy' })` in the Cypress support file repoints it. Cypress's `selectorPriority` has no effect here — that list only orders attributes when Cypress itself generates a selector for you.

saying these in an interview costs you the question

  • Thinks cy.findByRole is a built-in Cypress command
  • Expects cy.getByRole to exist after installing the add-on
  • Registers the commands in setupNodeEvents instead of the support file
  • Assumes findByTestId reads data-cy without configuring Testing Library