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(
  'a.b[1]',
)
expect({ a: { b: ['x', 'y'] } }).to.nested.include({
  'a.b[1]': 'y',
})

Or using the current subject

cy.wrap({ name: { first: 'Joe' } }).should(
  'have.nested.property',
  'name.first',
)

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' } })
  .its('name.first')
  .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
cy.wrap(1).should(
  'not.satisfy',
  (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">
  <li>Joe</li>
  <li>John</li>
  <li>Anna</li>
  <li>Mary</li>
</ul>
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
    .true
})
cy.log('**should satisfy with a message**')
cy.get('#people li')
  .its('length')
  .should('satisfy', isEven, 'even number of people in the list')

Members

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])

Extensible

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')

Sealed

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')

Frozen

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' }))
  .should('be.frozen')
  // frozen objects are also sealed
  .and('be.sealed')

Finite

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

cy.wrap(NaN).should('not.be.finite')
cy.wrap(Infinity).should('not.be.finite')
cy.wrap(42).should('be.finite')

Tip: Lodash bundled with Cypress includes predicates checking numbers

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

Custom type tag

checking built-in types

expect('foo').to.be.a('string')
expect({ a: 1 }).to.be.an('object')
expect(null).to.be.a('null')
expect(undefined).to.be.an('undefined')
expect(new Error()).to.be.an('error')
expect(Promise.resolve()).to.be.a('promise')
expect(new Float32Array()).to.be.a('float32array')
expect(Symbol()).to.be.a('symbol')

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

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