Jest测试中如何验证指定Promise在超时时间内无resolve无reject
1. 自定义.not.toComplete匹配器实现方案
Jest的expect.extend扩展机制完全支持实现这类自定义匹配器,核心逻辑是将待校验的Promise与指定时长的超时定时器对比,判断目标Promise是否在指定时间内完成。
匹配器扩展代码
expect.extend({ // 校验Promise是否在指定毫秒数内完成(支持.not取反) async toCompleteWithin(targetPromise, timeoutMs) { let isCompleted = false; let completeType, completeValue; // 监听目标Promise的完成状态 const wrappedTarget = Promise.resolve(targetPromise) .then(res => { isCompleted = true; completeType = 'resolve'; completeValue = res; }) .catch(err => { isCompleted = true; completeType = 'reject'; completeValue = err; }); // 等待超时时长 await new Promise(resolve => setTimeout(resolve, timeoutMs)); if (isCompleted) { return { pass: false, message: () => `预期Promise不会在${timeoutMs}ms内完成,但实际已${completeType},返回值为:${JSON.stringify(completeValue)}` }; } return { pass: true, message: () => `预期Promise会在${timeoutMs}ms内完成,但实际未触发任何完成状态` }; } });
使用示例
// 校验8000ms内Promise不会resolve或reject,阈值可根据测试超时时间自行调整 await expect(eventually("projects/1/userInfo/xyz", o => !!o, 800)).not.toCompleteWithin(8000);
如果需要避免目标Promise后续运行产生的副作用,可以给eventually方法增加取消逻辑(比如传入AbortSignal),匹配器返回时触发取消信号即可。
2. 测试即将超时的触发器实现方案
Jest当前未提供原生的「超时前触发」钩子,其内部超时逻辑执行后会直接标记测试失败,不会提前暴露回调入口。你可以通过自定义定时器模拟该能力:设置一个比测试超时时间短100~500ms的定时器,触发后执行你需要的自定义逻辑。
示例代码
test ('User information is not distributed to a project where the user is not a member', async () => { await collection("userInfo").doc("xyz").set({ displayName: "blah", photoURL: "https://no-such.png" }); const targetProm = eventually("projects/1/userInfo/xyz", o => !!o, 800); let isTestPassed = false; // 测试超时设为9999ms,提前500ms触发自定义逻辑 const preTimeoutTrigger = setTimeout(() => { // 此处编写超时前的自定义逻辑,判断符合条件则标记测试通过 isTestPassed = true; }, 9500); // 目标Promise提前完成则抛出错误 targetProm.finally(() => { clearTimeout(preTimeoutTrigger); if (!isTestPassed) throw new Error('目标Promise在超时前意外完成'); }); // 等待测试状态确认 await new Promise(resolve => { const checkStatus = () => isTestPassed ? resolve() : setTimeout(checkStatus, 50); checkStatus(); }); }, 9999);
如果需要原生支持该能力,确实可以向Jest提交功能请求,目前官方没有提供对应的公开API。
补充说明
由于JS原生Promise不支持外部取消,所有方案都需要你在eventually的内部实现中兼容超时/取消逻辑,比如通过AbortSignal接收外部取消信号,终止内部的轮询等操作,避免产生无意义的后续执行。
内容的提问来源于stack exchange,提问作者akauppi
相关产品推荐
相关产品推荐

