Jest中mockResolvedValueOnce适配await,mockRejectedValueOnce失效
Jest异步Action测试失败场景的问题解决
问题背景
测试初始化已执行jest.mock('axios');,成功场景的测试用例可正常运行,但失败场景使用mockRejectedValueOnce模拟请求失败时,expect语句并未等待dispatch执行完成,导致断言失败;若将mockRejectedValueOnce替换为mockResolvedValueOnce,测试结果则与成功用例一致。
成功用例代码
it('Dispatches SNACK_BAR after submitting ForgotPassword', async () => { let store = configureStore({ reducer: { auth: authReducer, snackBar: snackBarReducer }}) axios.post.mockResolvedValueOnce({headers: { authorization: 'asdasdasdas' }, status: 200}); await store.dispatch(forgotPasswordActions.forgotPasswordPost('test@test.com', (path) => {})) expect(store.getState().snackBar).toEqual({"message": "Check your email for a reset link", "severity": "success", "timestamp": store.getState().snackBar.timestamp}); });
失败用例代码
it('Dispatches SNACK_BAR after submitting ForgotPassword with an error', async () => { let store = configureStore({ reducer: { auth: authReducer, snackBar: snackBarReducer }}) axios.post.mockRejectedValueOnce({response: {headers: { authorization: 'asdasdasdas' }, status: 500, data: { error: 'Error' }}}); await store.dispatch(forgotPasswordActions.forgotPasswordPost('test@test.com', (path) => {})) expect(store.getState().snackBar).toEqual({"message": "Error", "severity": "error"}); })
被测试的Action函数
export const forgotPasswordPost = ({ email }, navigate) => async dispatch => { axios.post(`${ROOT_URL}/auth/password`, { "user": { email: email }}) .then(response => { return dispatch({ type: SNACK_BAR, payload: ["Check your email for a reset link", "success"]}) }) .catch(response => { return dispatch({ type: SNACK_BAR, payload: ["Error", "error"]}); }) }
问题原因
虽然forgotPasswordPost被定义为async函数,但函数内部并未返回axios.post的Promise链。async函数默认会在执行完同步代码后返回一个已resolved的Promise,不会等待axios.post的异步操作完成。
成功场景可能因mockResolvedValueOnce的执行时机巧合,让断言在dispatch完成后触发;但失败场景中,mockRejectedValueOnce触发的catch逻辑会在await store.dispatch完成后才执行,导致断言时状态还未更新。
解决方案
修改Action函数,返回axios.post的Promise链,让async函数的Promise与异步请求的生命周期绑定,确保await能等待到then/catch中的dispatch执行完成:
export const forgotPasswordPost = ({ email }, navigate) => async dispatch => { // 新增return,返回axios的Promise链 return axios.post(`${ROOT_URL}/auth/password`, { "user": { email: email }}) .then(response => { return dispatch({ type: SNACK_BAR, payload: ["Check your email for a reset link", "success"]}) }) .catch(response => { return dispatch({ type: SNACK_BAR, payload: ["Error", "error"]}); }) }
或者用async/await语法重构Action,代码可读性更强:
export const forgotPasswordPost = ({ email }, navigate) => async dispatch => { try { const response = await axios.post(`${ROOT_URL}/auth/password`, { "user": { email: email }}) return dispatch({ type: SNACK_BAR, payload: ["Check your email for a reset link", "success"]}) } catch (response) { return dispatch({ type: SNACK_BAR, payload: ["Error", "error"]}); } }
修改后重新执行测试,失败场景的expect语句会等待dispatch完成后再执行断言,即可得到正确结果。
内容的提问来源于stack exchange,提问作者Mike
相关产品推荐
相关产品推荐

