Vite+TS+Redux项目Vitest测试App组件时Async Thunk报错求助
解决Vitest测试Redux Async Thunk时的"Actions must be plain objects"错误
问题重现
在Vite+TypeScript+Redux Toolkit项目中,使用Vitest测试App组件渲染时,调用dispatch(fetchAirports())触发异步Thunk时抛出错误:
Error: Actions must be plain objects. Use custom middleware for async actions
报错位置在App.tsx的dispatch(fetchAirports())调用处。
原因分析
这个错误的核心是测试环境中使用的Redux Store未加载Redux Toolkit内置的thunk中间件。Redux本身不支持异步action,Redux Toolkit的configureStore默认已包含thunk中间件,但如果测试时手动创建未正确配置的store实例,就会丢失该中间件,导致无法处理async thunk。
解决方案
1. 确保Redux Store配置正确
确认你的store配置文件(如store.ts)使用Redux Toolkit的configureStore创建,而非原生Redux的createStore:
// store.ts import { configureStore } from '@reduxjs/toolkit'; import airportsReducer from './features/airports/airportsSlice'; export const store = configureStore({ reducer: { airports: airportsReducer, }, // configureStore默认已包含thunk,无需手动添加 }); export type RootState = ReturnType<typeof store.getState>; export type AppDispatch = typeof store.dispatch;
2. 测试文件中正确配置Store
在Vitest测试文件中,通过configureStore创建测试专用store,确保thunk中间件被加载:
// App.test.tsx import { render } from '@testing-library/react'; import { Provider } from 'react-redux'; import { configureStore } from '@reduxjs/toolkit'; import App from './App'; import airportsReducer from './features/airports/airportsSlice'; describe('App Component', () => { it('renders without crashing', () => { const testStore = configureStore({ reducer: { airports: airportsReducer, }, }); render( <Provider store={testStore}> <App /> </Provider> ); }); });
3. 模拟Async Thunk(孤立测试推荐)
若不想触发真实API请求,可模拟async thunk的返回:
// App.test.tsx import { render } from '@testing-library/react'; import { Provider } from 'react-redux'; import { configureStore } from '@reduxjs/toolkit'; import App from './App'; import airportsReducer from './features/airports/airportsSlice'; import { fetchAirports } from './features/airports/airportsSlice'; // 模拟fetchAirports thunk jest.mock('./features/airports/airportsSlice', () => ({ ...jest.requireActual('./features/airports/airportsSlice'), fetchAirports: jest.fn().mockReturnValue({ type: 'mock/action' }), })); describe('App Component', () => { it('dispatches fetchAirports on mount', () => { const testStore = configureStore({ reducer: { airports: airportsReducer, }, }); const spyDispatch = jest.spyOn(testStore, 'dispatch'); render( <Provider store={testStore}> <App /> </Provider> ); expect(spyDispatch).toHaveBeenCalledWith(fetchAirports()); }); });
4. 组件中使用正确的Dispatch类型
在组件中指定AppDispatch类型,避免隐性类型错误:
// App.tsx import { useDispatch } from 'react-redux'; import { useEffect } from 'react'; import { AppDispatch } from './store'; import { fetchAirports } from './features/airports/airportsSlice'; function App() { const dispatch = useDispatch<AppDispatch>(); useEffect(() => { dispatch(fetchAirports()); }, [dispatch]); return ( // 组件内容 ); } export default App;
关键检查点
- 所有测试用Store必须通过
configureStore创建,禁止使用原生createStore - 不要随意移除Redux Toolkit的默认中间件
- 模拟async thunk时,确保返回符合Redux规范的action对象
内容的提问来源于stack exchange,提问作者Ilia Popov
相关产品推荐
相关产品推荐

