如何使用Jest测试包含createAsyncThunk的Redux Toolkit slice
Redux Toolkit 含 createAsyncThunk 的 Slice 测试方案
你遇到的超时问题核心原因有两个:一是未 mock 服务层/网络请求,测试环境发起真实 HTTP 请求导致超时;二是异步 thunk 执行时未等待流程结束就做断言,或 slice 本身未编写对应 thunk 状态的 reducer 处理逻辑。
首先先修正你贴出代码里的遗漏项:当前combineExtraReducers仅处理了getData.fulfilled状态,完全没有添加postData对应的pending/fulfilled/rejected状态处理,这会导致即使thunk执行成功,store状态也不会更新。补全逻辑参考:
const combineExtraReducers = ( builder: ActionReducerMapBuilder<MyState>, ) => { builder.addCase(getData.fulfilled, (state, { payload }) => { state.data = payload; }); // 补全postData的状态处理 builder .addCase(postData.fulfilled, (state, { payload }) => { state.data = payload; }) .addCase(postData.rejected, (_, { payload }) => { // 按业务需求处理错误,比如存储错误信息 console.error('提交失败', payload); }); };
接下来分两种场景写测试,第一种是纯reducer测试(无异步逻辑,完全不会出现超时问题,适合验证状态更新逻辑),第二种是thunk集成测试(验证完整调用流程)。
1. 纯Reducer测试(最简单,无异步)
这种方式不需要运行thunk的异步逻辑,直接手动构造thunk生成的action对象传入reducer即可,全程同步执行:
import { dataSlice, postData } from './对应slice文件路径'; describe('dataSlice 状态逻辑', () => { const initialState = { data: [] }; it('处理postData.fulfilled时应正确更新data字段', () => { const mockPayload = [{ id: 1, content: '测试数据' }]; const action = { type: postData.fulfilled.type, payload: mockPayload }; const nextState = dataSlice.reducer(initialState, action); expect(nextState.data).toEqual(mockPayload); }); it('处理postData.rejected时应按预期处理错误', () => { const mockErrorMsg = '提交失败'; const action = { type: postData.rejected.type, payload: mockErrorMsg }; const nextState = dataSlice.reducer(initialState, action); // 按你的错误处理逻辑写断言即可 }); });
2. Thunk 集成测试(验证完整流程)
如果需要验证thunk内部的服务调用、参数传递、错误分支逻辑,需要配置测试用store,mock掉服务层方法,并且必须await dispatch的执行结果,否则异步逻辑未执行完测试就会结束,触发超时或断言失败:
import { configureStore } from '@reduxjs/toolkit'; import { dataSlice, postData } from './对应slice文件路径'; import { MyService } from './MyService对应路径'; // mock整个MyService类,不发起真实请求 jest.mock('./MyService对应路径', () => ({ MyService: jest.fn().mockImplementation(() => ({ postData: jest.fn() })) })); // 生成测试用store的工具函数 const createTestStore = () => configureStore({ reducer: { data: dataSlice.reducer } }); describe('postData 异步thunk', () => { let mockPostData: jest.Mock; beforeEach(() => { // 每个用例前清空mock记录,避免用例间互相影响 jest.clearAllMocks(); // 获取mock实例上的postData方法 const serviceInstance = (MyService as jest.Mock).mock.instances[0]; mockPostData = serviceInstance.postData as jest.Mock; }); it('请求成功时应调用服务并更新store状态', async () => { const mockResult = [{ id: 2, content: '服务返回数据' }]; const testParam = { data: [{ content: '提交的测试数据' }] }; // 配置mock返回成功结果 mockPostData.mockResolvedValue(mockResult); const store = createTestStore(); // 核心:必须加await等待thunk全流程执行完成 await store.dispatch(postData(testParam)); // 验证服务被正确调用 expect(mockPostData).toHaveBeenCalledWith(testParam.data); // 验证store状态更新正确 expect(store.getState().data.data).toEqual(mockResult); }); it('请求失败时应返回rejectWithValue包装的错误信息', async () => { const testError = new Error('网络连接异常'); const testParam = { data: [] }; // 配置mock返回失败 mockPostData.mockRejectedValue(testError); const store = createTestStore(); const dispatchResult = await store.dispatch(postData(testParam)); // 验证thunk走了rejected分支 expect(dispatchResult.type).toBe(postData.rejected.type); // 验证错误信息正确 expect(dispatchResult.payload).toBe('网络连接异常'); }); });
超时问题排查点
- 确认
await store.dispatch(thunkAction)没有遗漏,不加await会导致测试用例提前结束,异步逻辑未执行完成。 - 确认服务层/axios被正确mock,没有发起真实网络请求。如果直接mock axios,写法和mock MyService一致,
jest.mock('axios')后给对应请求方法配置mock返回值即可。 - 检查thunk内部是否有未捕获的同步异常,比如
MyService初始化时抛出错误会导致流程中断。 - 若mock逻辑较复杂,可适当延长单测超时时间,比如在用例末尾加
, 10000将超时时间改为10秒,正常mock正确的场景不需要调整该配置。
内容的提问来源于stack exchange,提问作者Codehan25
相关产品推荐
相关产品推荐

