如何用Jest/Sinon测试返回Promise的getBanana方法是否被调用?
解决Jest/Sinon测试中Promise+定时器导致断言失败的问题
我来帮你梳理下问题所在,以及对应的解决办法:
首先,你的测试有两个核心问题导致断言始终失败:
1. 微任务队列未被刷新,断言时机过早
Jest的runOnlyPendingTimers()会同步执行定时器里的代码,但getBanana()返回的Promise属于微任务,会被放到微任务队列中,不会在定时器执行完成后立刻执行。如果不等待微任务队列处理完毕,断言的时机可能过早,导致结果不符合预期。
2. thing变量的作用域错误
看你原代码里的if (thing),这个thing如果是类的实例属性(比如this.thing),那你测试里定义的全局let thing = true完全不会影响到类内部的判断——定时器里的条件永远不满足,自然不会执行getBanana,断言肯定是false。
修正后的测试代码
结合这两个问题,我给你调整后的测试代码:
test('init function calls getBanana', async () => { // 1. 创建类的实例,而不是直接调用原型上的方法 const testInstance = new TheClass(); // 2. 如果thing是实例属性,设置为true(根据你实际的变量作用域调整) testInstance.thing = true; // 3. 直接存根实例上的getBanana方法,简化spy的使用 const getBananaStub = sinon.stub(testInstance, 'getBanana').resolves(); jest.useFakeTimers(); // 调用实例的初始化方法 testInstance.initStuffAfterLoad(); // 执行待处理的定时器 jest.runOnlyPendingTimers(); // 等待微任务队列执行完成,确保所有异步逻辑收尾 await Promise.resolve(); // 用stub直接断言是否被调用 expect(getBananaStub.called).toBeTruthy(); // 恢复存根 getBananaStub.restore(); });
额外优化建议
- 不需要单独定义
getBananaSpy,Sinon的stub本身就具备spy的所有功能,可以直接用getBananaStub.called来判断是否被调用,简化代码。 - 如果你的
thing是全局变量,那测试里的let thing = true需要确保和原代码里的thing是同一个变量(比如原代码里是window.thing,那测试里也要设置window.thing = true)。 - 如果你用的是Jest 27+,也可以用
jest.runAllTicks()来强制刷新微任务队列,效果和await Promise.resolve()类似。
内容的提问来源于stack exchange,提问作者aroundtheworld
相关产品推荐
相关产品推荐

