Jasmine异步Mock函数二次调用后测试停滞问题排查
问题
在Jasmine中测试包含两次异步弹窗确认的删除流程时遇到异常:
getDialogAnswer是封装jQuery对话框的Promise异步函数,实际业务代码运行正常- 测试中两次
await调用该函数,Mock定义为jasmine.createSpy().and.returnValues('yes','yes')(也试过返回Promise.resolve('yes')) - 测试函数为
async类型,expect(getDialogAnswer).toHaveBeenCalledTimes(2)断言通过,但后续expect(mainPart.clearSelected).toHaveBeenCalledTimes(1)等4个断言全部失败(显示调用0次) - 最后
expect(unsaved_changes).toBe(true)断言显示Expected null to be true,但控制台输出unsaved_changes为true,说明业务逻辑已执行 - 特殊场景验证:将第二次
await getDialogAnswer改为直接赋值deleteProject = 'yes',所有断言通过;去掉第二次的await,测试通过但实际业务代码无法正常执行 - 环境:Jasmine HTML独立Specrunner,测试过4.6、5.0Beta版本
可能的解决思路
1. 确保Jasmine捕获所有异步操作完成时机
虽然使用了async/await,但Jasmine的断言执行时机可能和微任务队列存在偏差,可尝试两种方式修正:
- 使用
done回调显式标记测试完成:it("should delete on yes + yes", (done) => { (async () => { // 原有测试逻辑 // ...两次调用getDialogAnswer的代码 expect(getDialogAnswer).toHaveBeenCalledTimes(2); expect(mainPart.clearSelected).toHaveBeenCalledTimes(1); // 其他断言... expect(unsaved_changes).toBe(true); done(); // 告诉Jasmine测试已完成 })().catch(err => done.fail(err)); // 捕获异步错误 }); - 添加微任务触发:在所有断言前添加一行,强制清空微任务队列:
await new Promise(resolve => setTimeout(resolve, 0));
2. 调整Mock函数的Promise返回逻辑
改用callFake确保每次调用都返回独立的Promise实例,避免returnValues可能存在的时序问题:
const getDialogAnswer = jasmine.createSpy('Mock_getDialogAnswer').and.callFake(() => { return Promise.resolve('yes'); });
如果需要针对不同调用次数做区分,可结合计数器:
let callCounter = 0; const getDialogAnswer = jasmine.createSpy('Mock_getDialogAnswer').and.callFake(() => { callCounter++; // 如需不同返回值可在这里判断,比如第二次返回不同内容 return Promise.resolve('yes'); });
3. 检查业务代码中第二次确认后的异步逻辑
确认第二次弹窗后的删除操作(比如mainPart.clearSelected)是否包含未被等待的异步逻辑:
- 如果
mainPart.clearSelected是异步函数,需确保它返回Promise,且业务代码中使用await等待其完成 - 测试中也要确保等待所有业务异步操作结束后再执行断言
4. 验证Jasmine配置
检查独立Specrunner的HTML配置,确保没有禁用异步测试支持(默认是开启的),比如是否存在影响异步执行的自定义配置项。
内容的提问来源于stack exchange,提问作者DonPedro
相关产品推荐
相关产品推荐

