You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为什么基于Promise的Jest异步测试需要使用return关键字?

为什么Jest文档要求异步测试必须返回Promise,但实际不返回断言也能执行?

问题场景

Jest官方给出的异步测试示例如下:

test("Asynchronous code", () => {
    return Promise.resolve("data").then(res => {
        expect(res).toBe("data");
    });
});

文档中明确说明,这类测试必须返回Promise,理由是:

测试会在Promise返回的then()回调执行前就完成。

但实际测试时,我发现无论是否返回Promise,断言结果都一致。甚至写了一个本应失败的无返回测试:

test("Asynchronous code", () => {
    Promise.resolve("data").then(res => {
        expect(res).toBe("a failure");
    });
});

测试结果依然正确报错:

FAIL  Cours-Jest/--test--/test.js
  ● Asynchronous code

    expect(received).toBe(expected) // Object.is equality

    Expected: "a failure"
    Received: "data"

既然断言已经正确执行,为什么文档要求必须返回Promise?我忽略了什么?


原因解析

你看到的一致结果只是偶然情况,本质是因为你的Promise是立即resolve的,回调函数执行速度足够快,在Jest标记测试完成前就跑完了。但这种写法存在严重的竞态条件,完全不可靠:

1. 异步操作延迟时的问题

如果Promise的回调执行较慢(比如包含定时器、网络请求等延迟操作),Jest会在回调执行前就结束测试,此时断言不仅不会影响测试结果,甚至报错都不会被捕获。比如:

test("Async test without return", () => {
    new Promise(resolve => {
        // 模拟1秒后才resolve的异步操作
        setTimeout(() => resolve("data"), 1000);
    }).then(res => {
        expect(res).toBe("a failure");
    });
});

运行这个测试,Jest会直接输出PASS,但1秒后断言才会执行并报错——此时测试已经结束,这个错误不会被Jest捕获,导致你误以为测试通过,实际上断言根本没起到作用。

2. Jest的异步检测逻辑

Jest判断测试是否完成的核心逻辑是:

  • 如果测试函数返回Promise,Jest会等待这个Promise状态变更(resolve/reject)后,再结束测试并处理断言结果;
  • 如果没有返回Promise,Jest会认为测试是同步的,函数执行完就直接标记测试完成,不会等待任何未处理的异步操作。

你之前的测试能正常报错,只是因为Promise的微任务在Jest的测试结束逻辑之前就执行了,但这属于JavaScript事件循环的偶然顺序,不是Jest的刻意处理。

3. 关于.resolves/.rejects的情况

即使使用.resolves/.rejects语法,本质上还是依赖返回Promise让Jest等待。如果不返回,同样会出现上述竞态问题——只是你的测试用例中异步操作太快,才看起来结果一致。


总结

文档要求返回Promise的核心目的是让Jest明确知道测试中存在异步操作,必须等待异步流程完成后再结束测试,确保所有断言都能被执行并正确捕获结果。你看到的“无返回也能正常执行”只是极端场景下的偶然情况,在实际的异步业务逻辑中,这种写法会导致测试结果不可靠,甚至出现漏测的情况。

内容的提问来源于stack exchange,提问作者Lex

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 01:40:30