React项目中如何用Jest测试Promise的then回调函数调用
测试React中Promise回调函数调用的问题解决
这个问题我太熟了!本质是Jest默认同步执行测试逻辑,在signupAPI返回的Promise完成并触发then回调前,测试就已经跑完了断言,所以handleSuccess还没被调用,导致测试失败。先回顾下你的代码:
待测试代码
const handleSubmit = (handleSuccess, handleErrors) => { signupAPI(user) .then(handleSuccess) .catch(handleErrors); };
原测试代码(存在异步未等待问题)
test('should call handleSuccess', () => { signupAPI.mockImplementation((user) => Promise.resolve(user)); const handleSuccess = jest.fn(); const handleErrors = jest.fn(); handleSubmit(handleSuccess, handleErrors); expect(signupAPI).toHaveBeenCalled(); // 测试通过 expect(handleSuccess).toHaveBeenCalled(); // 测试失败 });
下面给你三种可行的解决方式:
方法1:用async/await等待异步操作
这是最直观的方式,要么修改handleSubmit让它返回Promise,要么等待微任务队列清空:
方案A:修改原函数返回Promise
先调整handleSubmit,让它返回API调用的Promise链:
const handleSubmit = (handleSuccess, handleErrors) => { return signupAPI(user) .then(handleSuccess) .catch(handleErrors); };
然后测试时用await等待handleSubmit完成:
test('should call handleSuccess', async () => { signupAPI.mockImplementation((user) => Promise.resolve(user)); const handleSuccess = jest.fn(); const handleErrors = jest.fn(); await handleSubmit(handleSuccess, handleErrors); expect(signupAPI).toHaveBeenCalled(); expect(handleSuccess).toHaveBeenCalled(); });
方案B:不修改原函数,等待微任务
如果不想改动原函数,也可以用await Promise.resolve()来等待所有微任务(包括Promise的then回调)执行完毕:
test('should call handleSuccess', async () => { signupAPI.mockImplementation((user) => Promise.resolve(user)); const handleSuccess = jest.fn(); const handleErrors = jest.fn(); handleSubmit(handleSuccess, handleErrors); await Promise.resolve(); // 等待微任务队列清空 expect(signupAPI).toHaveBeenCalled(); expect(handleSuccess).toHaveBeenCalled(); });
方法2:使用Jest的done回调
Jest支持传入done参数,当你确认异步操作完成后调用它,告诉Jest测试可以结束了:
test('should call handleSuccess', (done) => { signupAPI.mockImplementation((user) => Promise.resolve(user)); // 在handleSuccess的mock函数里执行断言并触发done const handleSuccess = jest.fn(() => { expect(signupAPI).toHaveBeenCalled(); expect(handleSuccess).toHaveBeenCalled(); done(); // 告知Jest测试完成 }); const handleErrors = jest.fn(); handleSubmit(handleSuccess, handleErrors); });
这种方式不需要修改原函数,直接在回调触发时完成断言。
方法3:让测试函数返回Promise
Jest会自动等待测试函数返回的Promise完成,所以可以返回一个Promise来延迟断言:
test('should call handleSuccess', () => { signupAPI.mockImplementation((user) => Promise.resolve(user)); const handleSuccess = jest.fn(); const handleErrors = jest.fn(); handleSubmit(handleSuccess, handleErrors); // 返回Promise,等待微任务执行后再断言 return Promise.resolve().then(() => { expect(signupAPI).toHaveBeenCalled(); expect(handleSuccess).toHaveBeenCalled(); }); });
最后还要提醒一句:你的handleSubmit里用到了user变量,如果这是组件内的状态,测试时要确保它被正确初始化或者mock,不然可能会影响signupAPI的调用逻辑哦。
内容的提问来源于stack exchange,提问作者nkpatel
相关产品推荐
相关产品推荐

