Jest嵌套Promise中断言无返回,报零断言调用错误求助
我完全懂你遇到的这个困扰——明明嵌套Promise里的断言语法在其他场景能跑,但到了Jest里就报错“Expected one assertion to be called but received zero assertion calls.”,核心原因其实是Jest没有正确追踪到嵌套Promise的完成状态,导致它在断言执行前就提前结束了测试。
问题根源
Jest判断异步测试是否完成的逻辑很明确:要么测试函数返回一个Promise,要么你调用done回调,要么用async/await语法。如果你的嵌套Promise没有被正确链式返回,外层的then会默认返回undefined(不是Promise),Jest就会误以为测试已经完成,而内层的Promise此时还在异步执行,断言自然没机会被触发。
举个典型的错误写法:
test('实时API测试', () => { fetch('/first-endpoint') .then(res => res.json()) .then(data => { // 这里没有return内层Promise,Jest看不到它 fetch('/second-endpoint') .then(res => res.json()) .then(result => { expect(result).toEqual('预期值'); }); }); });
三种解决方案
1. 正确链式返回Promise
把内层的Promise通过return传递出去,让整个测试函数返回完整的Promise链,Jest就会一直等待到整个链执行完毕:
test('实时API测试', () => { // return整个Promise链 return fetch('/first-endpoint') .then(res => res.json()) .then(data => { // return内层Promise,延续链式调用 return fetch('/second-endpoint') .then(res => res.json()) .then(result => { expect(result).toEqual('预期值'); }); }); });
2. 用async/await简化(最推荐)
嵌套Promise用async/await改写会更清晰,也能彻底避免链式返回的遗漏问题,Jest会自动等待所有await操作完成:
test('实时API测试', async () => { // 第一步请求 const firstRes = await fetch('/first-endpoint'); const firstData = await firstRes.json(); // 第二步请求(基于第一步结果的话也能直接用) const secondRes = await fetch('/second-endpoint'); const secondData = await secondRes.json(); // 断言会在所有异步操作完成后执行 expect(secondData).toEqual('预期值'); });
3. 使用done回调(兼容老写法)
如果一定要保留嵌套Promise的写法,可以用Jest提供的done参数,在最后一个断言执行后调用done(),明确告诉Jest测试结束。注意要处理错误分支,不然会导致测试超时:
test('实时API测试', (done) => { fetch('/first-endpoint') .then(res => res.json()) .then(data => { fetch('/second-endpoint') .then(res => res.json()) .then(result => { expect(result).toEqual('预期值'); done(); // 必须调用done标记测试完成 }) .catch(err => done(err)); // 错误时也要调用done,避免超时 }) .catch(err => done(err)); });
为什么其他场景能正常运行?
可能那些场景的运行环境(比如浏览器、Node.js脚本)会自动追踪所有异步任务的完成,但Jest作为测试框架,为了保证测试的可靠性,要求必须显式告知它异步操作的边界——毕竟测试需要明确知道什么时候该结束,不能无限等待。
内容的提问来源于stack exchange,提问作者Sean

