如何用Jest和Enzyme测试含Route的React App组件路由渲染?
看起来你遇到了React组件测试中路由和上下文依赖的常见问题,我来帮你一步步解决。
首先,咱们先分析下问题根源:
- 你的
<App>被多个上下文提供者(Provider、ConnectedRouter、LanguageProvider)包裹,用shallow渲染时无法自动处理这些上下文,而且Route组件在浅渲染下不会自动匹配路由并渲染其render内容。 - 用
mount/render时报错store未定义,是因为你的组件(或其子组件)依赖Redux store,但测试时没有提供对应的上下文。
接下来给你两种可行的解决方案,推荐第一种,因为它更贴近真实运行环境:
方案一:用mount完整渲染并提供所有必要上下文
这种方法会模拟你的应用真实的渲染层级,提供Redux store、语言上下文和路由环境,确保路由匹配和组件渲染都能正常工作。
步骤如下:
- 先创建一个Mock的Redux store(如果你的根reducer已经存在,可以直接用;如果只是测试,也可以写一个极简的mock reducer):
import { createStore } from 'redux'; // 导入你的根reducer,或者写一个mock的空reducer const mockReducer = (state = {}, action) => state; const mockStore = createStore(mockReducer); // 模拟LanguageProvider需要的messages const mockMessages = {}; // 根据你的实际需求填充必要的翻译文本
- 修改测试用例,把
<App>包裹在所有必要的提供者和MemoryRouter中:
describe('<App />', () => { describe('should render expected components after redirect', () => { let wrapper; beforeEach(() => { wrapper = mount( <Provider store={mockStore}> <LanguageProvider messages={mockMessages}> <MemoryRouter initialEntries={[URL_PREFIX]}> <App /> </MemoryRouter> </LanguageProvider> </Provider> ); }); it('renders <MapContainer />', () => { expect(wrapper.find(MapContainer)).toHaveLength(1); }); it('renders <NavigationBox />', () => { expect(wrapper.find(NavigationBox)).toHaveLength(1); }); it('renders <FeedbackMessage />', () => { expect(wrapper.find(FeedbackMessage)).toHaveLength(1); }); }); });
为什么这个能行?
mount会完整渲染所有嵌套组件,包括路由重定向(从URL_PREFIX跳转到${URL_PREFIX}/restaurants)。- 我们提供了
Provider和LanguageProvider,满足了组件的上下文依赖,不会再出现store未定义的错误。 MemoryRouter的initialEntries设置了初始路由,确保路由匹配逻辑正常触发。
方案二:用shallow并手动模拟路由匹配(适合单元测试)
如果你只想测试<App>本身的逻辑,不想渲染所有子组件,可以手动给<App>传递路由匹配的props,跳过路由上下文的处理:
describe('<App />', () => { describe('when matched with the module route', () => { let wrapper; beforeEach(() => { // 模拟Route匹配后的props,对应重定向后的路由 const mockMatch = { path: `${URL_PREFIX}/:module?/:id?`, url: `${URL_PREFIX}/restaurants`, isExact: true, params: { module: 'restaurants' } }; // 浅渲染App并传入mock的match props wrapper = shallow(<App match={mockMatch} />); }); it('renders <MapContainer />', () => { expect(wrapper.find(MapContainer)).toHaveLength(1); }); // 其他测试用例同理 }); });
注意:这种方法会跳过路由重定向的测试逻辑,只测试<App>在路由匹配时的组件渲染,适合单独测试<App>的内部逻辑,不适合测试路由跳转流程。
为什么你之前的shallow+dive不行?
- 前两次
dive()分别穿透了MemoryRouter和<App>,但<App>内部的MuiThemeProvider是一个组件,第三次dive()会尝试穿透到div宿主组件,所以报错TypeError: ShallowWrapper::dive() can not be called on Host Components。 - 更关键的是,
shallow不会处理路由上下文,所以<Route>组件不会自动匹配路由并渲染其render内容,导致你找不到<MapContainer>等子组件。
内容的提问来源于stack exchange,提问作者Isla
相关产品推荐
相关产品推荐

