100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
Testing

Chaining and Retry-ability

Understand how Cypress commands chain together via the command queue and how built-in retry-ability makes assertions resilient to asynchronous UI changes.

Selectors & CommandsIntermediate8 min readJul 10, 2026
Analogies

Chaining and Retry-ability

Every Cypress command returns a chainable subject that the next command in the same statement automatically receives. Writing cy.get('.card').find('.title').should('contain', 'Cypress') builds a single command chain where each command operates on exactly what the previous one yielded, letting you drill from a broad selection down to a specific assertion in one readable line.

🏏

Cricket analogy: A relay of fielders throwing the ball from the boundary to the keeper, each one receiving exactly what the last one threw, mirrors how cy.get('.card').find('.title') passes its resulting subject directly into the next chained command.

How the Command Queue Works

Cypress commands aren't promises you await directly; they're enqueued into an internal command queue when your test function runs, and Cypress's driver executes them one at a time, waiting for each to resolve (including any retries) before starting the next. This is why placing a console.log() between chained commands logs immediately, before the commands themselves have actually executed.

🏏

Cricket analogy: A bowling coach writes out today's entire training plan before the session starts, and each drill runs only when its turn comes, similar to how Cypress enqueues cy.get(), cy.click(), and cy.type() as a plan and executes them one at a time, not instantly when written.

javascript
cy.get('.todo-list')
  .find('li')
  .should('have.length', 3)
  .and('contain', 'Buy milk');

Retry-ability: Assertions Re-run Automatically

When you chain .should() or .and() onto a command like cy.get(), Cypress doesn't just check the assertion once. It re-runs the preceding command (re-querying the DOM) and re-evaluates the assertion repeatedly until it passes or the timeout is reached. This is what lets a test written immediately after a button click reliably wait for a success message that appears only after an async API call resolves.

🏏

Cricket analogy: DRS keeps re-analyzing ball-tracking data until it can confidently rule lbw, rather than accepting the first frame it sees, similar to how cy.get('.spinner').should('not.exist') keeps re-querying the DOM until the assertion passes or the timeout runs out.

Retry-ability applies to the command immediately preceding a should()/and() chain, not to every command in the test. Commands like cy.click() or cy.type() run once; it's the querying commands like cy.get() combined with an assertion that retry.

Where Retry-ability Breaks Down

Using cy.then(($el) => { ... }) hands you the current subject as a one-time snapshot for imperative code, but Cypress has no way to know what condition inside that callback should be true, so it won't re-run the callback automatically. Any assertion made on data pulled out inside .then() needs its own explicit retry logic or should be moved before the .then() into a chained should().

🏏

Cricket analogy: Once a batter is given out and walks off, no amount of replaying the same delivery changes that recorded outcome, similar to how cy.then(($el) => {...}) captures a snapshot of the element at that instant, so it won't automatically re-run if the DOM later changes.

Resist the urge to add cy.wait(3000) to 'fix' a flaky test. Arbitrary waits either aren't long enough on a slow CI run or waste time on a fast one. Let Cypress's built-in retry-ability (should()/and()) wait for the actual condition instead of a fixed duration.

  • Cypress commands chain by passing their yielded subject to the next command in the same statement.
  • Commands are enqueued and run sequentially from an internal queue, not the instant they're written in code.
  • should() and and() re-run the preceding command and re-check the assertion until it passes or times out.
  • Retry-ability applies to the command right before should()/and(), not the entire test.
  • cy.then() captures a one-time snapshot of the subject and does not automatically retry.
  • Avoid cy.wait(ms) as a fix for timing issues; rely on retry-able assertions instead.

Practice what you learned

Was this page helpful?

Topics covered

#Testing#CypressStudyNotes#TestingQA#ChainingAndRetryAbility#Chaining#Retry#Ability#Command#StudyNotes#SkillVeris