Skip to content

should vs expect in Cypress

Both .should() and expect() ultimately use the same Chai assertion library underneath, but they behave very differently in a Cypress test. This lesson compares them directly with practical guidance on choosing between them.

Same Assertions, Different Retry Behavior

.should('have.text', 'Done') and expect($el.text()).to.equal('Done') can express the exact same check, but .should() is chained onto the Cypress command queue and retried automatically, while expect() runs immediately, once, at the moment its line executes inside a callback.

This difference is the entire reason .should() is generally preferred: it tolerates the natural asynchronous timing of a real application, while expect() requires the value to already be correct the instant it runs.

// Preferred: retried automatically until it passes
cy.get('[data-cy="status"]').should('have.text', 'Done');

// Works, but must already be correct when .then() fires
cy.get('[data-cy="status"]').then(($el) => {
  expect($el.text()).to.equal('Done');
});

If the status text updates a moment after the element appears, the .should() version tolerates that; the expect() version may fail depending on timing.

Chai Assertion Chainers Used by Both

.to.equal(value)
.to.have.length(n)
.to.include(value)
.to.be.true
.to.have.property('key', value)
  • Both .should() and expect() support the full range of Chai chainers, equal, include, have.length, and more.
  • .should('be.true') and expect(x).to.be.true express the same check in each style.
  • Chai-jQuery adds DOM-specific chainers like have.text, be.visible, and have.class, usable with either style.
  • Sinon-Chai adds spy/stub chainers like have.been.called, most useful with .should() on a spy alias.

should vs expect Cheat Sheet

A direct comparison to help decide which style fits a given check.

Scenario Recommended Style
Checking element visibility/text/class .should()
Checking a value after custom computation expect() inside .then()
Multiple related checks on the same element .should() chained with .and()
Asserting on a spy or stub call count .should('have.been.calledOnce')
Complex conditional logic before asserting .then() + expect()

Chaining Multiple Assertions with .and()

When multiple checks apply to the same element, .and() (an alias for .should()) keeps them readable in a single chain, and each one is still retried as part of the same automatic retry cycle.

cy.get('[data-cy="submit"]')
  .should('be.visible')
  .and('not.be.disabled')
  .and('have.text', 'Place Order');

When expect() Is the Right Call

expect() is appropriate once you have already reached a stable point, typically inside a .then() after a .should() has confirmed a precondition, and need to perform custom computation or multiple related checks on a plain JavaScript value rather than a DOM element directly.

cy.get('[data-cy="cart-items"] li').should('have.length.greaterThan', 0).then(($items) => {
  const prices = [...$items].map((el) => Number(el.dataset.price));
  const total = prices.reduce((sum, price) => sum + price, 0);
  expect(total).to.be.greaterThan(0);
});

The .should() guarantees at least one item exists before the .then() computes and asserts on a derived value.

Common Mistakes

  • Defaulting to expect() for every assertion out of habit, losing retry-ability without a good reason.
  • Not realizing .and() is simply an alias for .should(), and using it inconsistently for readability only.
  • Writing complex multi-step logic directly inside a .should(callback) when a .then() + expect() would be clearer.
  • Asserting inside a .then() without a preceding .should() guaranteeing the precondition needed for that logic to be valid.

Key Takeaways

  • .should() and expect() use the same underlying Chai assertions but differ in retry behavior.
  • .should()/.and() retry automatically and should be the default choice.
  • expect() runs once and is best reserved for custom computed values inside .then().
  • Combining a .should() precondition with a follow-up .then() + expect() is a common, reliable pattern.

Pro Tip

Use .should() to guarantee a precondition (like "at least one item exists") before dropping into .then() for custom computation, this keeps the retry-sensitive part of the check automatic while still allowing complex logic where it's genuinely needed.