Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cypress request with retry

Tags:

cypress

In a Cypress test, I need to validate an action by calling an external API. The API call will always return results (from some prior run), so I can't simply call once and validate the result. I need to retry some number of times until I find a match for the current run with an overall timeout/failure. The amount of time necessary to get a current result varies greatly; I can't really just put a crazy long wait before this call.

See comments in snippet below; as soon as I try a request in a loop it's never called. I got the same result using cy.wait. Nor can I wrap the actual request in another function that returns Cypress.Promise or similar, that just pushes the problem up one stack frame.

Cypress.Commands.add("verifyExternalAction", (someComparisonValue) => { 

    const options = {
      "url": some_url,
      "auth": { "bearer": some_apikey },
      "headers": { "Accept": "application/json" }
    };

    //// This works fine; we hit the assertion inside then.
    cy.request(options).then((resp) => {
      assert.isTrue(resp.something > someComparisonValue);
    });

    //// We never enter then.
    let retry = 0;
    let foundMatch = false;
    while ((retry < 1) && (!foundMatch)) {
      cy.wait(10000);
      retry++;
      cy.request(options).then((resp) => {
        if (resp.something > someComparisonValue) {
          foundMatch = true;
        }
      });
    }
    assert.isTrue(foundMatch);

});
like image 612
dudeNumber4 Avatar asked Jan 07 '19 17:01

dudeNumber4


People also ask

How do you use retry in Cypress?

You can configure this in the Cypress configuration by passing the retries option an object with the following options: runMode allows you to define the number of test retries when running cypress run. openMode allows you to define the number of test retries when running cypress open.

How do you run the same test multiple times in Cypress?

Run a test N times #Using the plugin cypress-grep you can select the test to run using a part of its title and skip all other tests and specs. In our GitHub Actions workflow repeat-test. yml we can pass the grep and burn parameters connecting the user interface to the test run.

How do you use waitFor in Cypress?

Usage. describe('Test suite', () => { it('A test description', () => { cy. visit('https://localhost:3000'); // The page does an animation or loading // so we now need to wait for an element // with the id `main-body` cy. waitFor('#main-body'); // Now we have instant access to the #main-body element cy.

What is callback function in Cypress?

callbackFn (Function)Pass a function that takes the previously yielded subject as its first argument.


1 Answers

  1. You can't mix sync (while loop; assert.isTrue outside the cy commands...) and async work (cy commands). Read introduction to cypress #Chains-of-Commands
  2. Your first request is asserting the resp.something value and if it fails the whole command fails, thus no more retries.
  3. You're doing async work and you can't await cypress commands (which you weren't doing, anyway) thus you need recursion, not iteration. In other words, you can't use a while loop.

Something likes this should work:

Cypress.Commands.add("verifyExternalAction", (someComparisonValue) => {

    const options = {
        "url": some_url,
        "auth": { "bearer": some_apikey },
        "headers": { "Accept": "application/json" }
    };

    let retries = -1;

    function makeRequest () {
        retries++;
        return cy.request(options)
            .then( resp => {
                try {
                    expect( resp.body ).to.be.gt( someComparisonValue );
                } catch ( err ) {

                    if ( retries > 5 ) throw new Error(`retried too many times (${--retries})`)
                    return makeRequest();
                }
                return resp;
            });
    }

    return makeRequest();
});

If you don't want cypress to log all the failed expectations during retries, don't use expect/assert which throws, and do regular comparison (and possibly assert only at the end in a .then callback chained to the last makeRequest() call).

like image 50
dwelle Avatar answered Oct 12 '22 13:10

dwelle