
本文详解如何为 Cypress 中的递归 API 轮询函数(如 resWait)添加精确的 2 分钟全局超时机制,通过 Promise 封装 + Promise.race() 实现安全中断,避免无限循环和测试挂起。
本文详解如何为 cypress 中的递归 api 轮询函数(如 `reswait`)添加精确的 2 分钟全局超时机制,通过 promise 封装 + `promise.race()` 实现安全中断,避免无限循环和测试挂起。
在 Cypress 测试中,常需轮询后端接口直至某状态达成(例如交易确认)。原始代码使用递归调用 resWait() 配合 cy.wait(3000) 实现轮询,但缺乏超时保护——若服务异常或状态永不满足,测试将无限等待,最终超时失败且难以定位原因。真正的超时控制不是简单调用 setTimeout,而是将整个轮询逻辑封装为 Promise,并与一个“超时 Promise”竞争执行结果。
✅ 正确做法:Promise 封装 + Promise.race()
首先,将轮询逻辑改造成返回 Promise 的异步函数,并支持最大重试次数与递归延迟:
function resWait(maxRetries = 40, delayMs = 3000) {
// 每次轮询最多尝试 40 次(40 × 3s = 120s = 2 分钟),兼顾精度与容错
return new Promise((resolve, reject) => {
const attempt = (retryCount = 0) => {
cy.req('GET', `${url}/transaction/acquirertrxquery?acquirerId=1&dateFrom=${actualDate}%2000:00:00&dateTo=${actualDate}%2023:59:00&externalId=${idExternal}`)
.then((res) => {
const status = res.body?.content?.[0]?.trxConfirmationStatus?.description;
if (status === 'Confirmed') {
resolve('Transaction confirmed successfully.');
} else if (retryCount >= maxRetries) {
reject(new Error(`Timeout after ${maxRetries} attempts (${maxRetries * delayMs / 1000}s). Transaction not confirmed.`));
} else {
// 延迟后重试(注意:Cypress 中应使用 cy.wait 而非原生 setTimeout)
cy.wait(delayMs).then(() => attempt(retryCount + 1));
}
})
.catch((error) => {
if (retryCount >= maxRetries) {
reject(new Error(`Request failed on final attempt: ${error.message}`));
} else {
cy.wait(delayMs).then(() => attempt(retryCount + 1));
}
});
};
attempt();
});
}接着,使用 Promise.race() 将轮询 Promise 与一个 2 分钟超时 Promise 竞争:
// 创建 2 分钟超时 Promise(拒绝并抛出明确错误)
const timeoutPromise = new Promise((_, reject) => {
setTimeout(() => {
reject(new Error('Global timeout of 2 minutes exceeded. Polling aborted.'));
}, 2 * 60 * 1000); // 120000 ms
});
// 启动带超时保护的轮询
Promise.race([resWait(), timeoutPromise])
.then((message) => {
cy.log(message); // ✅ 成功:输出确认信息
})
.catch((error) => {
cy.log('⚠️ Polling failed:', error.message);
// 此处可继续执行“超时后流程”,如断言失败、截图、跳转下一步等
});⚠️ 关键注意事项
- 不要在 Cypress 命令链中混用原生 setTimeout/await:Cypress 命令是异步队列,await sleep(3000) 会破坏命令链时序,导致不可预测行为。务必使用 cy.wait(3000)。
- maxRetries 是核心超时参数:示例中 40 × 3000ms = 120s,比单纯 setTimeout(120000) 更可靠——它确保最后一次请求发出后仍能完成响应处理,而非粗暴中断。
- 错误分类处理:网络错误、空响应、状态不匹配应区分对待;超时错误需提供清晰上下文,便于调试。
- Cypress 特性兼容:此方案完全兼容 Cypress 的自动重试、命令日志和截图能力,失败时可在 Cypress UI 中直观查看每一步。
✅ 总结
为递归轮询函数添加超时,本质是将同步思维(setTimeout(func, 120000))升级为异步编排(Promise.race(polling, timeout))。通过封装 Promise、显式控制重试边界、配合 cy.wait 保持 Cypress 兼容性,即可实现健壮、可观测、易维护的超时策略。这不仅是技术实现,更是编写高可靠性 E2E 测试的关键实践。

















