In Cypress, how do you write an `expect()` assertion inside a `.then()` callback?
answer
- The callback gets the previous command's subject
- No assertion library to import
- cy.get yields a jQuery collection, not one element
- Chai's expect and assert are spec globals
- expect($rooms).to.have.length(4) inside then
basics
~10 sCypress makes Chai's expect and assert global in every spec, so inside .then(($rooms) => ...) you call expect($rooms).to.have.length(4) straight on the yielded subject. A throw from that expect fails the test at that command.
solid answer
~40 s`.then(cb)` hands the previous command's subject to plain JavaScript. After `cy.get('[data-cy=room-card]')` that subject is a **jQuery collection** of the matched room cards, so `expect($rooms).to.have.length(4)` or `expect($rooms.eq(0).text()).to.contain('Deluxe King')` both work. Nothing needs importing: Cypress bundles Chai and exposes `expect` (BDD) and `assert` (TDD) as spec globals, plus the Chai-jQuery and Sinon-Chai extensions. A failing assertion throws an `AssertionError`; Cypress catches it, fails the test at that command and renders the assertion in the Command Log. Chai's optional second argument labels a check — `expect($rooms, 'four rooms listed').to.have.length(4)` — which is worth using once a block holds several assertions. Use the explicit form when you must compute first: parse `$210.00` into a number, compare two elements, check sort order. The catch is that `.then()` runs its callback once and does not retry.
code
javascript · 16 linesit('lists four rooms in ascending price order', () => {
cy.visit('/rooms')
cy.get('[data-cy=room-card]').then(($rooms) => {
expect($rooms, 'four rooms listed').to.have.length(4)
// .find() and .toArray() here are jQuery's; Cypress bundles jQuery
const rates = $rooms
.find('[data-cy=nightly-rate]')
.toArray()
.map((el) => Number(el.textContent.replace(/[^0-9.]/g, '')))
expect(rates).to.deep.equal([...rates].sort((a, b) => a - b))
expect(rates[0]).to.be.greaterThan(0)
})
})go deeper
Be ready to write the block from memory: cy.get(...).then(($rooms) => expect($rooms).to.have.length(4)). Say plainly that Chai ships inside Cypress, so there is nothing to import.
Explain what the callback actually receives — a jQuery collection, not one element — and how you reach the value you assert on: toArray, eq, text, or a raw node by index.
Show judgment about when an explicit block beats a chained assertion: computed values, relationships between two elements, and groups of checks against one query result.
Own the convention. Decide which checks a suite writes explicitly, how long such a block may grow before its first throw hides the rest, and how the team reviews them.
## The subject the callback receives Every Cypress command yields a **subject** to the next step in the chain. `.then(cb)` is the escape hatch that hands that subject to plain JavaScript: whatever the previous command produced arrives as the callback's first argument. After `cy.get('[data-cy=room-card]')` on a hotel room list, that argument is a **jQuery collection** wrapping every matched element — not a single element, and not a raw DOM node. After `cy.request()` it is the response object, after `cy.fixture('rooms')` the parsed fixture, after `.invoke('text')` a string. Inside the callback you are in ordinary synchronous JavaScript, so anything you could do to that value in a console you can do here. Cypress names the two assertion styles after the subject. A chained `.should('have.length', 4)` is an **implicit** assertion: Cypress supplies the subject for you. Writing `expect($rooms).to.have.length(4)` inside a callback is an **explicit** assertion: you name the subject yourself. This is the second form. ## Chai is already in the spec — both `expect` and `assert` You do not import an assertion library into a Cypress spec. Cypress bundles **Chai** and installs both of its interfaces as globals on the spec window: - `expect(value)` — Chai's BDD interface, the one nearly every Cypress example uses: `expect(rates).to.deep.equal([180, 210, 240, 320])`. - `assert` — Chai's TDD interface, so `assert.equal(actual, expected, message)` and its siblings work in the same callback if your team prefers that style. Two extensions ride along. **Chai-jQuery** teaches Chai about jQuery objects, which is why `expect($rooms).to.have.length(4)` and `expect($rooms.first()).to.contain('Deluxe King')` read naturally; **Sinon-Chai** adds the spy and stub chainers. A failing `expect` throws an `AssertionError`; Cypress catches the throw, fails the test at that command and renders the assertion in the Command Log. There is no accumulate-and-report mode — the first throw ends the test. Chai's `expect` also takes an optional message as its second argument, and Cypress surfaces it: `expect($rooms, 'four rooms listed').to.have.length(4)` labels that entry so a failure names the check instead of only printing two values. In a block with several assertions it is the cheapest possible diagnostics. ## Getting from a jQuery subject to the value you want | what you want | how you get it | |---|---| | how many rooms matched | `expect($rooms).to.have.length(4)` | | the text of one card | `expect($rooms.eq(0).text()).to.contain('Deluxe King')` | | a raw DOM node | `$rooms[0]`, then `.className`, `.dataset.rate`, `.textContent` | | every rate as a number | jQuery's `.toArray()`, then a plain `.map()` | | one attribute | `$rooms.eq(0).attr('data-rate')` — jQuery's `.attr()` | Two habits keep these blocks readable. Convert to plain JavaScript early: jQuery's `.toArray()` returns a real array, so `.map()`, `.filter()` and `.sort()` behave the way you expect. And assert on the computed value rather than on the collection, because Chai's failure output for a number or an array of strings is far easier to read than its output for a jQuery object. ## When an explicit block earns its place Reach for one when the check is not expressible as a single chainer: 1. **A computed value.** The nightly rate renders as `$210.00`, the assertion is about the number 210, and the parse has to live somewhere. 2. **A relationship between values.** The booking total equals the nightly rate times the number of nights the date picker spans — no built-in chainer relates two subjects. 3. **A group of checks on one snapshot.** Length, the first card's name, and the full list of room types, all asserted against the same query result. 4. **A shape the chainers do not cover.** Sort order, a numeric tolerance, a regular expression over a derived string. For anything a chainer already says — visibility, class, attribute, exact text — the chained form is shorter and reads better. Explicit blocks are the tool for the cases the vocabulary misses, not a replacement for it. ## The one cost to keep in mind `.then()` invokes its callback **once**. The moment the subject is available Cypress calls it, the assertions inside get a single attempt, and nothing above `.then()` in the chain re-runs. That is the right trade when the page has settled and the wrong trade when the value you are computing is still arriving — which is why the identical code moved into a `.should()` callback behaves differently.
- In a Cypress `.then()` callback, how do you get a plain array out of the jQuery subject?Call jQuery's `.toArray()` on it: `$rooms.toArray()` returns a real array of DOM nodes, so `.map()`, `.filter()` and `.sort()` behave normally. Indexing works too — `$rooms[0]` is a raw element while `$rooms.eq(0)` keeps you in jQuery. Assert on the derived array rather than the collection; Chai's output for an array of strings is far easier to read than its output for a jQuery object.
- What does the second argument to Chai's `expect()` do in a Cypress spec?It is a message label. `expect($rooms, 'four rooms listed').to.have.length(4)` attaches that text to the assertion, and Cypress shows it on the Command Log entry and in the failure output. In a callback holding several `expect()` calls it is the cheapest way to make a failure say which check broke instead of only which two values differed.
saying these in an interview costs you the question
- Thinks Chai must be imported into the spec file
- Calls the .then() argument a single DOM element
- Believes a failed expect only logs a warning
- Expects an expect inside .then() to keep retrying
- Assumes only chained should strings can assert in Cypress