如何在Next.js+Redux Toolkit的Jest测试中获取Store并渲染组件?
问题
在Next.js项目中用Jest做组件集成测试时,必须用Redux的Provider包裹组件才能让组件访问Store,但项目依赖next-redux-wrapper同步客户端与服务端Store,_app.tsx里通过wrapper.useWrappedStore全局提供Store,不清楚如何在测试文件中获取Store并正确渲染组件。
相关代码片段:
测试文件(tests/Calculator.test.tsx)
import { Calculator } from "@/components"; import { cleanup, render } from "@testing-library/react"; import { Provider } from "react-redux"; afterEach(cleanup); it("Check Calculator", () => { const CalculatorTest: React.FunctionComponent = () => { return ( <Provider store={}> <Calculator></Calculator> </Provider> ); }; const { queryAllByTestId } = render(<CalculatorTest />); expect(queryAllByTestId("calc-test")).toBeTruthy(); });
_app.tsx
const MyApp: FC<AppProps> = ({ Component, ...rest }) => { const { store, props } = wrapper.useWrappedStore(rest); return ( <Provider store={store}> <Head> <title>Calculator</title> </Head> <Component {...props.pageProps} /> </Provider> ); }; export default MyApp;
redux/store.ts
const makeStore = () => configureStore({ reducer: { history: calculatorHistoryReducer, }, devTools: true, }); const store = makeStore(); export type RootStore = ReturnType<typeof makeStore>; export type RootState = ReturnType<RootStore["getState"]>; export type AppDispatch = typeof store.dispatch; export type AppThunk<ReturnType = void> = ThunkAction< ReturnType, RootState, unknown, Action<string> >; export const useAppDispatch: () => typeof store.dispatch = useDispatch; export const wrapper = createWrapper<RootStore>(makeStore);
解决方案
方法1:直接生成测试用Store
利用已定义的makeStore工厂函数,直接创建测试专用Store,再用Provider包裹组件:
import { Calculator } from "@/components"; import { cleanup, render } from "@testing-library/react"; import { Provider } from "react-redux"; import { makeStore } from "@/redux/store"; afterEach(cleanup); it("Check Calculator", () => { // 创建全新的测试用Store const testStore = makeStore(); const { queryAllByTestId } = render( <Provider store={testStore}> <Calculator /> </Provider> ); expect(queryAllByTestId("calc-test")).toBeTruthy(); });
方法2:借助next-redux-wrapper的wrapper获取Store
通过wrapper对象创建Store,还能模拟服务端预加载数据的逻辑:
import { Calculator } from "@/components"; import { cleanup, render } from "@testing-library/react"; import { Provider } from "react-redux"; import { wrapper } from "@/redux/store"; import { fetchHistory } from "@/redux/actions"; // 导入需要的action afterEach(cleanup); it("Check Calculator", async () => { // 通过wrapper生成Store const testStore = wrapper.createStore(); // 模拟getServerSideProps中的数据预加载 await testStore.dispatch(fetchHistory()); const { queryAllByTestId } = render( <Provider store={testStore}> <Calculator /> </Provider> ); expect(queryAllByTestId("calc-test")).toBeTruthy(); });
方法3:封装自定义render工具函数
为了避免重复编写Provider包裹逻辑,封装一个带Store的render工具:
创建test-utils.tsx:
import { render, RenderOptions } from "@testing-library/react"; import { Provider } from "react-redux"; import { makeStore, RootStore } from "@/redux/store"; const customRender = ( ui: React.ReactElement, { store = makeStore(), ...renderOptions }: RenderOptions & { store?: RootStore } = {} ) => { const Wrapper = ({ children }: { children: React.ReactNode }) => ( <Provider store={store}>{children}</Provider> ); return render(ui, { wrapper: Wrapper, ...renderOptions }); }; export * from "@testing-library/react"; export { customRender as render };
在测试文件中使用这个工具:
import { Calculator } from "@/components"; import { cleanup, render } from "@/test-utils"; import { makeStore } from "@/redux/store"; import { fetchHistory } from "@/redux/actions"; afterEach(cleanup); it("Check Calculator", async () => { // 可选:创建带初始数据的Store const testStore = makeStore(); await testStore.dispatch(fetchHistory()); const { queryAllByTestId } = render(<Calculator />, { store: testStore }); expect(queryAllByTestId("calc-test")).toBeTruthy(); });
关键说明
makeStore每次调用都会生成全新Store,保证测试之间的状态隔离。- 如果组件依赖服务端预加载的数据,需要在测试中手动dispatch对应的action初始化Store状态。
- 自定义render工具能统一处理Store包裹逻辑,减少重复代码。
内容的提问来源于stack exchange,提问作者tresor13
相关产品推荐
相关产品推荐

