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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 18:10:50