skip to content

Implicit Should Syntax

should and and chained straight onto a query, re-running it until the check passes or the timeout runs out, plus the bundled chainers and the false pass a negative check can give.

on this pageshow

explore

questions

5

In Cypress, what does .should('have.text', 'Deluxe King') do when chained onto cy.get()?

level: juniorimportance: must knowfreq 84%

answer

  1. the assertion is also the wait
  2. chained onto the query, not standalone
  3. chainer string first, expected value after
  4. budget comes from defaultCommandTimeout
  5. and() is the same command renamed

basics

~10 s

It attaches a Chai-jQuery assertion to the preceding cy.get(), and Cypress keeps re-evaluating the query and the check until the element's text matches or the four-second default command timeout expires, then fails the test.

solid answer

~40 s

`.should()` is Cypress's implicit assertion: you chain it straight onto a query and Cypress will not resolve that query until the assertion passes. `cy.get('[data-cy=room-name]').should('have.text', 'Deluxe King')` re-runs the lookup and the check repeatedly, so a room list that renders after an availability call needs no explicit wait. The first argument is a chainer string — `have.text`, `have.value`, `have.class`, `have.length`, `be.visible` — drawn from the bundled Chai, Chai-jQuery and Sinon-Chai; later arguments are the expected value, or a member name plus a value for `have.attr`. The retry budget is `defaultCommandTimeout`, 4000 ms out of the box, raised per call with `cy.get(selector, { timeout: 10000 })`. If it never passes, the command fails and reports the last actual value. `.and()` is the same command under a second name.

code

javascript · 21 lines
javascript
describe('room list', () => {
  it('shows the deluxe room once availability loads', () => {
    cy.visit('/rooms?checkin=2026-11-02&nights=3')

    // one 10s window, shared by the query and every assertion chained to it
    cy.get('[data-cy=room-card]', { timeout: 10000 })
      .should('have.length', 8)
      .and('be.visible')

    cy.get('[data-cy=room-card]')
      .first()
      .find('[data-cy=room-name]')
      .should('have.text', 'Deluxe King')

    // have.attr swaps the subject for the attribute's value
    cy.get('[data-cy=nightly-rate]')
      .first()
      .should('have.attr', 'data-currency')
      .and('equal', 'EUR')
  })
})

go deeper

for a junior

Be ready to write .should('have.text', ...) onto a cy.get() from memory and to say that Cypress keeps re-checking rather than asserting once.

for a middle

An interviewer expects you to name the four call signatures, say which bundled library each chainer comes from, and place the retry budget on the preceding command.

for a senior

Show when you would raise the timeout on a single query instead of globally, and how you read the last actual value out of a failed assertion to separate a slow render from a wrong selector.

for a principal

Own the convention for how assertions are written across a suite, so every reviewer reads a chain the same way and each timeout override is a deliberate decision rather than a copied line.

## What an implicit assertion is An **implicit assertion** is one you chain straight onto a Cypress query instead of writing it as a separate statement. The first argument to `.should()` is a **chainer string** — a dotted path such as `have.text`, `be.visible` or `have.length` — and any arguments after it are the expected value. ```javascript cy.get('[data-cy=room-name]').should('have.text', 'Deluxe King') ``` The assertion belongs to the `cy.get()` in front of it. Cypress does not treat that lookup as finished until `have.text` is satisfied, so a room list that only paints after the availability API responds needs no explicit wait in the test. The check *is* the wait. ## The four call signatures Picking the wrong shape is the most common beginner mistake on this API. | Signature | Example on a booking page | What it checks | | --- | --- | --- | | `.should(chainer)` | `.should('be.visible')` | a property-style chainer that takes no value | | `.should(chainer, value)` | `.should('have.text', 'Deluxe King')` | the chainer against one expected value | | `.should(chainer, method, value)` | `.should('have.attr', 'href', '/rooms/12')` | a named member, then its expected value | | `.should(callbackFn)` | `.should(($rooms) => { /* ... */ })` | a function Cypress re-runs until nothing inside it throws | Where each chainer comes from matters when you are reading a failure. Cypress bundles three assertion libraries and no import is needed: - **Chai** supplies the general ones — `have.length`, `equal`, `match`, `include`. - **Chai-jQuery** supplies the DOM ones — `have.text`, `have.value`, `have.class`, `be.visible`, `exist`. - **Sinon-Chai** supplies the test-double ones — `have.been.calledWith`, `have.been.calledOnce` — and they only make sense when the subject is a spy or stub. A chainer Cypress cannot resolve is **not** retried: it fails at once with a message saying the chainer was not found, so a typo like `have.txt` surfaces in milliseconds rather than after the full timeout. ## The retry window The assertion has no timeout of its own. `.should()` accepts no options object — the window comes from the command the assertion is attached to. - `defaultCommandTimeout` is the global budget, **4000 ms** as of Cypress 16. - `cy.get('[data-cy=room-card]', { timeout: 10000 })` raises it for that one lookup, and every assertion chained after it shares that same ten-second window. - `{ timeout: 0 }` switches retrying off, turning the check into a single synchronous look at the page. - Writing `.should('be.visible', { timeout: 10000 })` extends nothing, because `.should()` reads a second argument as the **expected value**, not as options. When the window closes the command fails, and the report shows the assertion together with the last actual value Cypress saw — the text it did find, or how many `[data-cy=room-card]` elements were present. That number is usually enough to tell a slow render apart from a wrong selector. ## Chaining a second check with `.and()` `.and()` is not a different command. In the Cypress driver both names are registered against the same implementation, so `.and()` is purely a readability alias: ```javascript cy.get('[data-cy=room-card]') .should('have.length', 8) .and('be.visible') ``` Two consequences follow: 1. Every assertion in the group sees the **same subject reference**. Some chainers replace it — `have.attr` yields the attribute's value, `have.css` yields the computed value — so anything chained after one of those is asserting on a string, not on an element. 2. Assertions run **in order**, and the first failure ends the group. If `have.length` never reaches 8, the `be.visible` check is never evaluated and never reported. ## Where candidates go wrong - Reaching for a fixed pause before the assertion. The retry window already covers a slow render; a pause only makes a passing test slower and a failing one no more informative. - Chaining `.should()` off `cy` itself. It is a child command and needs a subject, so `cy.should('exist')` is an error. - Assuming the assertion waits for "the page". It re-checks the specific subject the chain produced and nothing else. - Treating a passing negative chainer as proof of behaviour. `should('not.have.class', 'sold-out')` is satisfied the instant that class is absent — including before the app has rendered anything. The short version: `.should(chainer, value)` is an assertion that doubles as the wait, its budget is `defaultCommandTimeout` unless you override it on the query, and `.and()` is the same command spelled for readability.

  • What does Cypress do when the chainer string passed to .should() is not a real assertion?
    It fails immediately instead of retrying. Cypress walks the dotted chainer path against the assertion object, and a missing segment throws an error saying the chainer was not found, with retrying switched off. So `have.txt` surfaces in milliseconds rather than after the full `defaultCommandTimeout`. A lone English getter such as `.should('be', true)` is rejected the same way, because `be` is a connector, not an assertion.
  • Which chainers change what .should() yields to the next command in the chain?
    Most keep the subject: `cy.get('[data-cy=room-card]').should('be.visible')` still yields the elements. Value-extracting chainers replace it — `have.attr` yields the attribute's value and `have.css` yields the computed value — so an `.and('include', '/rooms')` after `have.attr` is asserting on a string. The callback form is the exception that never changes anything: whatever the callback returns is discarded and the original subject is yielded on.

It is the difference between glancing at a departures board once and standing in front of it until your gate appears - the assertion keeps looking until it passes or your time runs out.

saying these in an interview costs you the question

  • Says you must add a fixed wait before every assertion
  • Thinks .should() can be called directly on cy
  • Believes .should() takes its own timeout option
  • Cannot say where have.text or have.length come from
  • Assumes .and() behaves differently from .should()
open as a page

In Cypress, where do you put { timeout: 10000 } so a chained .should() retries longer?

level: middleimportance: must knowfreq 63%

basics

~10 s

Put it on the query, not the assertion: cy.get(selector, { timeout: 10000 }). That single window covers the lookup and every assertion chained after it, because .should() accepts no options object of its own.

open as a page

In Cypress, why does .should('be.visible').and('not.be.visible') on one cy.get() never pass?

level: middleimportance: should knowfreq 41%

basics

~20 s

Every assertion chained onto one cy.get() runs against the same subject reference in the same retry window, so a single element cannot satisfy both conditions at once. Split the checks into two cy.get() statements so Cypress re-queries between them.

open as a page

Why can a Cypress .should('not.exist') check pass even when the feature is broken?

level: seniorimportance: should knowfreq 46%

basics

~20 s

A negative chainer is satisfied the moment the thing is absent, including before the app has rendered anything at all. Cypress stops retrying on the first pass, so the check can succeed on an untouched page rather than on the state you meant to verify.

open as a page

In Cypress, why can .should('have.been.calledWith', args) pass on an earlier wrong call?

level: seniorimportance: nice to knowfreq 29%

basics

~20 s

Sinon-Chai's calledWith is satisfied if any recorded call matched those arguments, and it matches them partially rather than exactly. A spy called correctly once and wrongly afterwards still passes. Use the always, Once or Exactly variants when the whole history matters.

open as a page