Lesser Known Chai Assertions

See Chai BDD pageopen in new window and Cypress assertions

nested property

expect({ a: { b: ['x', 'y'] } }).to.have.nested.property(
expect({ a: { b: ['x', 'y'] } }).to.nested.include({
  'a.b[1]': 'y',

Or using the current subject

cy.wrap({ name: { first: 'Joe' } }).should(

You can even provide an expected value of the nested property.

cy.wrap({ name: { first: 'Joe' } })
  .should('have.nested.property', 'name.first', 'Joe')
  // yields the nested property value
  .should('equal', 'Joe')

The assertion is similar to the cy.itsopen in new window + should('equal', value) combination.

cy.wrap({ name: { first: 'Joe' } })
  .should('equal', 'Joe')

own property

You can check if an object or its prototype includes a property

Object.prototype.b = 2

expect({ a: 1 }).to.have.property('a')
// property "b" comes from the prototype
expect({ a: 1 }).to.have.property('b')

You can limit the assertion to the object's own properties

expect({ a: 1 }).to.have.own.property('a')
expect({ a: 1 }).to.not.have.own.property('b')

Using own.include you can check a "part" of an object with the values

expect({ a: 1 }).to.own.include({ a: 1 })
expect({ a: 1 })
  .to.include({ b: 2 })
  .but.not.own.include({ b: 2 })

The same can be done with a subject

cy.wrap({ name: 'Joe', age: 20 })
  .should('own.include', {
    name: 'Joe',
  // yields the original object
  .should('own.property', 'age')
  // yields the property value
  .should('be.closeTo', 20, 2)

Should satisfy

📺 You can watch the explanation for should satisfy assertion in the video Lesser Known Chai Assertions Like Satisfy A Predicateopen in new window.

expect(1).to.satisfy(function (num) {
  return num > 0
// you can add a string message after the predicate
cy.wrap(1).should('satisfy', (num) => num > 0, 'positive')
// you can flip the assertion and check
// if the predicate is NOT satisfied
  (num) => num < 0,
  'not negative',

It is convenient to use explicit should('satisfy', predicate) because you can add a nice log message.

<ul id="people">
const isEven = (n) => n % 2 === 0
cy.log('**should callback**')
cy.get('#people li').should(($li) => {
  expect($li.length % 2 === 0, 'even number of elements').to.be
cy.log('**should satisfy with a message**')
cy.get('#people li')
  .should('satisfy', isEven, 'even number of people in the list')


Checks if the subject array has the same items as the given array, the order does not matter.

cy.wrap([1, 2, 3]).should('have.members', [3, 2, 1])

If you want to check the order, use ordered

cy.wrap([1, 2, 3]).should('have.ordered.members', [1, 2, 3])


Checks if new properties can be added to the object

// A plain object is extensible
cy.wrap({ name: 'Joe' }).should('be.extensible')
// A sealed object is not extensible
cy.wrap(Object.seal({ name: 'Joe' })).should('not.be.extensible')


Checks if the object is sealed (no new properties, but existing properties can be assigned new values)

// A plain object is not sealed
cy.wrap({ name: 'Joe' }).should('not.be.sealed')
// A sealed object is sealed
cy.wrap(Object.seal({ name: 'Joe' })).should('be.sealed')


Checks if the object is frozen and nothing can be changed about it.

// A plain object is not frozen
cy.wrap({ name: 'Joe' }).should('not.be.frozen')
// A frozen object
cy.wrap(Object.freeze({ name: 'Joe' }))
  // frozen objects are also sealed


The object is a number that is neither a NaN nor Infinity


Tip: Lodash bundled with Cypress includes predicates checking numbers

cy.wrap(42).should('satisfy', Cypress._.isFinite, '_.isFinite')

Custom type tag

checking built-in types

expect({ a: 1 }).to.be.an('object')
expect(new Error()).to.be.an('error')
expect(new Float32Array()).to.be.a('float32array')

If we have our own custom types, we can confirm them

const myObj = {
  [Symbol.toStringTag]: 'myCustomType',
  .should('not.be.an', 'object')
  .and('to.be.a', 'myCustomType')