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);
});
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.
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.
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.
callbackFn (Function)Pass a function that takes the previously yielded subject as its first argument.
while
loop; assert.isTrue
outside the cy commands...) and async work (cy commands). Read introduction to cypress #Chains-of-Commands
resp.something
value and if it fails the whole command fails, thus no more retries.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).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With