Search code examples
github-actionscypresse2e-testingcypress-intercept

Verify number of Api calls in cypress


We are using segment in our application and I need to implement an E2E test in order to verify the number of segment calls, I must be sure that every event will be called only once. I've been searching for a while, I've found this command that verifies the number of api calls:

Cypress.Commands.add(`verifyCallCount`, (alias, expectedNumberOfCalls) => {
  const resolvedAlias = alias[0] === `@` ? alias.substring(1) : alias;

  cy.get(`${resolvedAlias}.all`, { timeout: 20000 }).then((calls) => {
    cy.wrap(calls.length).should(`equal`, expectedNumberOfCalls);
  });
});

I use this command after waiting for the api call:

cy.wait(`@${eventAlias}`, { timeout: 20000 })
  .then((interception) => {
    return JSON.parse(interception.request.body);
  })
  .then(() => cy.verifyCallCount(eventAlias, 1));

Here is also the place where I add my alias for the api call:

beforeEach(() => {
  cy.intercept('POST', 'https://api.segment.io/v1', (req) => {
    const body = JSON.parse(req.body);

    if (body.hasOwnProperty('type') && body.type === SampleEvent) {
      req.alias = eventAlias;
    }
  });
});

Using this approach, when I run the test on a local environment, it passes without any problem. but the same test fails on github's actions. and this is the error:

AssertionError: Timed out retrying after 10000ms: Expected to find element: `eventAlias.all`, but never found it.

I think that the .get() command is not being executed after .wait(), I tried to change the order of the commands, but it's not helping.

How can I fix this problem in github actions?

Is there any other way to verify the number of api calls in cypress?

I appreciate any help, thank you.


Solution

  • Your goal is to ensure only 1 API call.

    You will need the test to wait and see if a 2nd call occurs.

    it('accurately test that only one API call happens', () => {
    
      const numOfRequests = 1
    
      cy.intercept('**/api/*', cy.spy().as('api-spy'))
      
      cy.visit('/');
    
      cy.wait(1000)
      
      cy.get('@api-spy').its('callCount').should('equal', numOfRequests)
    
    })
    

    I tested with a simple page that deliberately calls twice, with a delay 100ms between calls,

    <script>
      fetch('api/1')
      setTimeout(() => fetch('api/2'), 100)  // delayed 2nd fetch we want to test for
    </script>
    

    Without the hard wait the test gives me a false pass.


    I also tried inverting the logic, but it still needs a hard wait to test correctly

    cy.intercept('**/api/*', cy.spy().as('api-spy'))
      
    cy.visit('/');
    
    cy.wait(1000)
      
    cy.get('@api-spy').its('callCount')
      .should('not.equal', 0)
      .and('not.equal', 2)                 // false pass without hard wait
    })
    

    Counting inside the routeHandler that checks body.type

    2nd alias for call count

    before(() => {
      cy.wrap(0).as('eventCount')
    })
    
    beforeEach(() => {
     cy.intercept('POST', 'https://api.segment.io/v1', (req) => {
          const body = JSON.parse(req.body);
    
          if (body.hasOwnProperty('type') && body.type === SampleEvent) {
            req.alias = eventAlias;
            cy.get('@eventCount').then(count => {
              cy.wrap(count + 1).as('eventCount')
            })
          }
        });
      });
    });
    
    it('checks the count', () => {
      cy.visit('/');
      cy.wait(1000)
      cy.get('@eventCount')
        .should('equal', 1)
    })
    

    Incrementing a global

    let eventCount = 0;
    
    beforeEach(() => {
     cy.intercept('POST', 'https://api.segment.io/v1', (req) => {
          const body = JSON.parse(req.body);
    
          if (body.hasOwnProperty('type') && body.type === SampleEvent) {
            req.alias = eventAlias;
            eventCount += 1
          }
        });
      });
    });
    
    it('checks the count', () => {
      cy.visit('/');
      cy.wait(1000)
        .then(() => {
          cy.wrap(eventCount)
            .should('equal', 1)
        })
    })