问题
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 (
whileloop;assert.isTrueoutside the cy commands...) and async work (cy commands). Read introduction to cypress #Chains-of-Commands - Your first request is asserting the
resp.somethingvalue and if it fails the whole command fails, thus no more retries. - You're doing async work and you can't
awaitcypress commands (which you weren't doing, anyway) thus you need recursion, not iteration. In other words, you can't use awhileloop.
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