craigmiller160
craigmiller160

Reputation: 6263

In Cypress, How Can I Validate That An Intercepted API Has Been Called (n) Times?

I'm using Cypress for its component tests. These are integration-style tests, I mount my whole app (React) and then click through it. All API calls are mocked using the Cypress Intercept feature. So far this has been an incredibly powerful solution, far more robust than any other FE testing suite I've worked with.

One limitation I've run into is with the intercepted API calls. I've got this scenario where I've got an API that is called twice during the flow of a given test. One of the conditions I want to validate is that it is, indeed, called twice, as the second call is triggered by the logic I am testing.

So the specific Cypress validation I want to work is this:

cy.get('#myButton').click(); // Triggers the second API call
cy.get('@myApi.all').should('have.length', 2);

Now, the problem is that the above code, on its own, fails because Cypress only registers a single call to the intercepted API named myApi. The reason for this is Cypress moves to immediately validate the number of calls to this intercepted API, rather than waiting for the action I just triggered in the UI.

The only way I know of to make the above code work is to add in explicit waiting, like this:

cy.get('#myButton').click(); // Triggers the second API call
cy.wait(300);
cy.get('@myApi.all').should('have.length', 2);

Because I am explicitly waiting 300ms after clicking the button, enough time passes for the second API call to occur and Cypress to register it, so the test passes.

I don't like this solution. I don't like adding explicit waits to my test code, it feels like a band-aid and will likely be error prone as it relies on execution timing to succeed. However, I simply do not know of a better option.

Upvotes: 1

Views: 2188

Answers (3)

jjhelguero
jjhelguero

Reputation: 2555

You can use cy.get('@myApi.all') but you'll have to be careful with using .all as it will only check the matching intercepts at that moment. So you would have to ensure both calls have been triggered by then.

Similar to this answer, Cypress comes bundled with Sinon-chai. You'll spy/stub and alias, trigger the first call and check the alias it was called once, wait until the second call is trigger by your app to check on the second call.

// spy/stub with alias your reach component call

cy.get('#myButton').click(); // Triggers the second API call
cy.get('@myApi').should('have.been.calledOnce')
// wait for your app to trigger the call
cy.get('@myApi').should('have.been.calledTwice')

Upvotes: -1

Alcock
Alcock

Reputation: 184

I'd suggest you don't use cy.get('@myApi.all') as it's undocumented.

With intercept there are many ways to use javascript in a straight-forward way, here is an example from the docs.

it('makes 3 calls', () => {
  let count = 0
  cy.intercept('/favorite-fruits', () => {
    // we are not changing the request or response here
    // just counting the matched calls
    count += 1
  })
  cy.visit('/fruits.html')
  // ensure the fruits are loaded
  cy.get('.favorite-fruits li').should('have.length', 5)

  cy.reload()
  cy.get('.favorite-fruits li').should('have.length', 5)

  cy.reload()
  cy.get('.favorite-fruits li').should('have.length', 5)
    .then(() => {
      // by now the count should have been updated
      expect(count, 'network calls to fetch fruits').to.equal(3)
    })
})

Upvotes: 4

Klea
Klea

Reputation: 170

That's almost right but instead of cy.wait(300) use cy.wait('@myApi') - because cy.get('@myApi') is non-waiting.

cy.get('#myButton').click()
cy.wait('@myApi')
cy.get('@myApi.all').should('have.length', 2)

Upvotes: 6

Related Questions