如何用Jest测试分发两个函数的Redux Action?
解决Redux Thunk Action的Jest测试问题
我来帮你搞定这个测试难题,你遇到的doSomething返回undefined、dispatch.mock.calls显示匿名函数的问题,根源在于mock的位置不正确和异步逻辑未正确等待,下面一步步修复:
1. 调整Jest Mock的位置
jest.mock会被Jest自动提升到所有import语句之前执行,如果你把它放在describe内部,会导致import的原模块没有被正确mock,这就是doSomething返回undefined的核心原因。
把jest.mock移到模块顶部,和import同级:
import doSomething from 'redux/do-something'; import doSomethingElse from 'redux/do-something-else'; import functionToTest from 'redux/function-to-test'; // 放在模块顶部,确保mock在import前生效 jest.mock('redux/do-something'); jest.mock('redux/do-something-else'); describe("functionToTest", ()=>{ // 测试代码放在这里 })
2. Mock Action Creator的返回值
Jest默认会把mock的模块替换成返回undefined的空函数,所以我们需要手动给doSomething和doSomethingElse设置返回值,这样才能追踪它们的调用和dispatch的参数。
根据你的action类型(普通action或thunk),设置对应的mock返回值:
- 如果是普通action creator(返回action对象):
doSomething.mockReturnValue({ type: 'DO_SOMETHING' }); doSomethingElse.mockReturnValue({ type: 'DO_SOMETHING_ELSE' }); - 如果是thunk action creator(返回函数):
// mock成返回一个可await的promise,确保异步流程能走完 doSomething.mockReturnValue(() => Promise.resolve()); doSomethingElse.mockReturnValue(() => Promise.resolve());
3. 正确等待异步逻辑
你的functionToTest返回的是一个async函数,测试时如果不await它的执行,测试会在异步操作完成前就结束,导致dispatch.mock.calls只捕获到中间的匿名函数(thunk函数),而不是最终的action。
在测试里添加await,等待整个异步流程完成:
完整修正后的测试代码
import doSomething from 'redux/do-something'; import doSomethingElse from 'redux/do-something-else'; import functionToTest from 'redux/function-to-test'; jest.mock('redux/do-something'); jest.mock('redux/do-something-else'); describe("functionToTest", ()=>{ const dispatch = jest.fn(); // 每次测试前重置mock,避免测试间的状态污染 beforeEach(() => { dispatch.mockClear(); doSomething.mockClear(); doSomethingElse.mockClear(); // 根据你的实际action类型选择下面的mock方式 // 普通action creator的mock: doSomething.mockReturnValue({ type: 'DO_SOMETHING' }); doSomethingElse.mockReturnValue({ type: 'DO_SOMETHING_ELSE' }); // 如果是thunk action creator,替换成这个: // doSomething.mockReturnValue(() => Promise.resolve()); // doSomethingElse.mockReturnValue(() => Promise.resolve()); }); test('dispatches doSomething followed by doSomethingElse', async ()=>{ // 等待异步action执行完成 await functionToTest()(dispatch); // 验证action creators被调用 expect(doSomething).toHaveBeenCalledTimes(1); expect(doSomethingElse).toHaveBeenCalledTimes(1); // 验证dispatch的调用顺序和参数 expect(dispatch).toHaveBeenCalledTimes(2); expect(dispatch).toHaveBeenNthCalledWith(1, { type: 'DO_SOMETHING' }); expect(dispatch).toHaveBeenNthCalledWith(2, { type: 'DO_SOMETHING_ELSE' }); }) })
关键问题解释
doSomething返回undefined:因为之前jest.mock放在describe内部,导致import的是原模块而非mock版本,加上Jest默认mock返回undefined,所以需要调整mock位置并手动设置返回值。dispatch.mock.calls显示匿名函数:因为没有await异步action的执行,测试提前结束,此时dispatch只接收到了thunk函数(而非最终的action对象),通过await确保整个异步流程完成后再验证。
内容的提问来源于stack exchange,提问作者Evanss
相关产品推荐
相关产品推荐

