Reputation: 841
Using Cypress Intercept to mock the routes and I want to verify the number of times the route was called. So far, I've found nothing in the docs for this. There's mention of cy.spy
but it only returns 1, every time. There's a {times:N}
object for the intercepted route, but it allows the route to match and succeed any number of times. It doesn't function as a call limit. This is such a common need that I'm sure I'm just missing something, tired eyes and all.
Spy:
cy.intercept({method: 'GET', url:'my-url', middleware:cy.spy().as('myspy')})
Times:
cy.intercept({method: 'GET', url:'my-url'}, {times:0})
Cypress Feature request: https://github.com/cypress-io/cypress/issues/16655
Upvotes: 7
Views: 11799
Reputation:
Cypress intercept is the spy.
The problem is when to check the call count.
For example, http://cypress.io/page-data
it('counts intercepts', () => {
let requestCounter = 0;
let responseCounter = 0;
cy.intercept('page-data/**/*', (req) => {
requestCounter += 1; // count requests
req.on('response', () => responseCounter += 1 ) // or count responses
})
cy.visit('https://www.cypress.io/')
cy.wait(5000).then(() => { // arbitrary wait
expect(requestCounter).to.eq(18) // since we don't know exactly
expect(responseCounter).to.eq(18) // what loads last
})
})
The answer given by Jennifer Shehane in the linked feature request shows another way using <alias>.all
,
it('counts intercepts', () => {
cy.intercept('page-data/**/*')
.as('pageData')
cy.visit('https://www.cypress.io/')
cy.get('@pageData.all')
.should('have.length', 18);
})
However, it does not consistently pass. About 1 in 5 runs on my machine fail because the cy.get() responds too early.
Ideally you should be able add a timeout, but this currently has no effect.
cy.get('@pageData.all', { timeout: 10000 }) // does not observe the timeout
Using cy.spy()
as a routeHandler allows a timeout to be set on the code that checks the call count.
it('counts intercepts', () => {
cy.intercept({ url: 'page-data/**/*', middleware: true }, req => {
req.on('response', (res) => {
res.setDelay(2000) // artificial delay to force failure
})
})
const spy = cy.spy();
cy.intercept('page-data/**/*', spy)
cy.visit('https://www.cypress.io/')
cy.wrap({}, { timeout: 10000 }) // adjust timeout to suit worst case page load
.should(() => {
console.log('testing spy') // see the retry happening (90+ logs)
expect(spy).to.be.callCount(18)
})
})
Upvotes: 15
Reputation: 2506
I found this problem to be a rare reason to use a local variable in Cypress:
it('Counts intercepts', () => {
let count = 0
cy.intercept('GET','/route', req => {
count = count+1;
req.reply({})
})
//... your tests
cy.get('body').then( () => {
expect(count,'Number of times intercepted').to.equal(1) //Assert intercepted only once
})
})
Upvotes: 4
Reputation: 43351
The following command works.
Note that cy.get
doesn't work with the leading @
but it feels more expected to me, so this will work with or without it.
Add this to /cypress/support/commands.js
Cypress.Commands.add(`verifyCallCount`, (alias, expectedNumberOfCalls) => {
const resolvedAlias = alias[0] === `@` ? alias.substring(1) : alias
cy.get(`${resolvedAlias}.all`).then((calls) => {
cy.wrap(calls.length).should(`equal`, expectedNumberOfCalls)
})
})
Usage:
cy.verifyCallCount(`@createLogEntry`, 3)
// Or
cy.verifyCallCount(`createLogEntry`, 3)
Upvotes: 1
Reputation: 6240
I was looking for the same thing because we have demo accounts that are not meant to ever reach/call our apis. After doing some research, this is what ended up working for me:
describe('demo accounts', () => {
beforeEach(function spyOnApiCalls() {
cy.intercept(/payment-platform/, cy.spy().as('coreApi'))
cy.intercept(/service-management/, cy.spy().as('mgmtApi'))
})
afterEach(function assertApisWereNotCalled() {
cy.get('@coreApi').its('callCount').should('equal', 0)
cy.get('@mgmtApi').its('callCount').should('equal', 0)
})
it('start test blocks', () => {...})
})
Note how we're passing and aliasing instances of the cy.spy
, then later asserting against them. IMO, this also reads pretty well with the its('callCount').should('equal', expectedCallCount)
.
Here we have beforeEach
and afterEach
blocks because it made sense in my case, but this approach seems pretty flexible and should work in many other scenarios.
Upvotes: 3