Spy called with an object

Imagine you are spying on a call using cy.spyopen in new window command. How do you check if the caller passed the right arguments? If you already read the examples from my Spies, Stubs & Clocks page, then keep reading.

📺 watch this recipe explained in the video Check If A Spy Was Called With The Right Objectopen in new window.

Primitive types

If the method is called with primitive arguments like strings, then it is simple to check.

<script>
  setTimeout(() => {
    console.log('Hello', 'world')
  }, 100)
</script>
cy.window()
  .its('console')
  .then((console) => {
    cy.spy(console, 'log').as('log')
  })
cy.get('@log').should('be.calledWith', 'Hello', 'world')

Exact object

If the method is called with an object, you can do two things: either yield it to the next assertion deep.equal or use the Sinon's built-in matching assertion.

<script>
  setTimeout(() => {
    console.log('User %o', { id: 123, name: 'Joe' })
  }, 100)
</script>
cy.window()
  .its('console')
  .then((console) => {
    cy.spy(console, 'log').as('log')
  })

We have set up the spy to be ready when the application calls console.log. We now need to confirm the log method was called with 2 arguments. The first argument is a primitive string User %o. That is easy to do:

cy.get('@log').should('be.calledWith', 'User %o')

How do we confirm the second argument? Sinon-Chaiopen in new window library calledWith uses deep.equal comparison by default, thus we can simply pass the object.

// use Sinon-Chai built-in object by value match
cy.get('@log').should('be.calledWith', 'User %o', {
  id: 123,
  name: 'Joe',
})

We can also confirm the call using placeholder Cypress.sinon.match.object and yield the argument to run more "assertions" using chained commands.

// yield the object to the next assertion
cy.get('@log')
  .should('be.calledWith', 'User %o', Cypress.sinon.match.object)
  .its('firstCall.args.1')
  .should('deep.equal', { id: 123, name: 'Joe' })

Partial object

If we know some properties of the object, we can use deep.include assertion on the yielded object.

<script>
  setTimeout(() => {
    console.log('User %o', { id: 123, name: 'Joe' })
  }, 100)
</script>
cy.window()
  .its('console')
  .then((console) => {
    cy.spy(console, 'log').as('log')
  })
cy.get('@log')
  .should('be.calledWith', 'User %o', Cypress.sinon.match.object)
  .its('firstCall.args.1')
  // imagine we do not know all object fields
  .should('deep.include', { name: 'Joe' })

Alternatively, if we know all the expected field names, we can use Sinon matchers as placeholders:

cy.get('@log').should('be.calledWith', 'User %o', {
  id: Cypress.sinon.match.number,
  name: 'Joe',
})

Tip: try using cy-spokopen in new window plugin for such assertions.

See also