问题
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);
});
回答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 - Your first request is asserting the
resp.something
value and if it fails the whole command fails, thus no more retries. - 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 awhile
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).
来源:https://stackoverflow.com/questions/54079106/cypress-request-with-retry