React Redux与Router组件Jest测试报错:无法读取pathname
问题排查:React Redux + Router测试中出现
TypeError: Cannot read properties of undefined (reading 'pathname') 我正在测试集成了React Redux和React Router的MovieDetail组件,点击主页面卡片会路由到该组件,运行测试时出现以下错误:
Uncaught [TypeError: Cannot read properties of undefined (reading 'pathname')]
相关代码如下:
目标路由组件MovieDetail
const MovieDetail = () => { const { id } = useParams(); const dispatch = useDispatch(); const movie = useSelector((state) => state.movies.selectedItem); const crews = useSelector((state) => state.movies.detailList); useEffect(() => { dispatch(fetchDetail(id, "movies", movieActions)); dispatch(fetchDetailList(id, "movies", "crews", movieActions)); }, [dispatch, id]); return ( <Box> <Card sx={{ display: "flex", justifyContent: "space-between", gap: "80px", margin: "80px 10px 10px 10px", }} > <Box sx={{ margin: "10px" }}> <Typography sx={{ fontSize: "25px" }}>{movie.title}</Typography> <Box sx={{ paddingLeft: "3px", marginTop: "20px", fontSize: "24px", display: "flex", justifyContent: "space-between", }} > <span> <img src="https://img.icons8.com/tiny-color/16/000000/star.png" alt="star icon" /> IMDB Rating <i></i>: {movie.imdbRating} </span> <span> IMDB Votes <i></i>: {movie.imdbVotes} </span> <span> Runtime <i></i> : {movie.runtime} </span> <span> Year <i></i> : {movie.year} </span> </Box> <Typography sx={{ margin: "20px" }}>{movie.plot}</Typography> </Box> </Card> </Box> ); }; export default MovieDetail;
Jest请求拦截器
rest.get("http://localhost:8080/api/movies/tt0389790", (req, res, ctx) => { return res( ctx.json({ id: "tt0389790", title: "Bee Movie", year: "2007", country: "United States", poster: "https://m.media-amazon.com/images/M/MV5BMjE1MDYxOTA4MF5BMl5BanBnXkFtZTcwMDE0MDUzMw@@._V1_SX300.jpg", }) ); }),
自定义测试包装器renderWithProviders
export function renderWithProviders( ui, { route = '/', history = createMemoryHistory({initialEntries: [route]}), preloadedState = {}, storeUtils = store(preloadedState), ...renderOptions } = {} ) { function Wrapper({ ui}) { return ( <Provider store={storeUtils}> <Router history={history}>{ui}</Router> </Provider> ); } return { store, ...render(ui, { wrapper: Wrapper, history, ...renderOptions }) }; }
测试用例
test("data is shown properly", async () => { renderWithProviders( <Route path="/api/movies/tt0389790"> {props =><MovieDetail {...props} />} </Route>, { route: '/api/movies/tt0389790', }, ); const moviePoster = await screen.findByAltText("Bee Movie"); expect(moviePoster).toBeInTheDocument(); const movieTitle = await screen.findByText("Bee Movie"); expect(movieTitle).toBeInTheDocument(); const movieYear = await screen.findByText("2007"); expect(movieYear).toBeInTheDocument(); });
实际路由配置
<Route path="/movies/:id" element={<MovieDetail />} />
错误原因与修复方案
1. 测试包装器的Wrapper组件参数错误
renderWithProviders中的Wrapper组件接收的是{ ui },但React Testing Library的wrapper组件实际接收的是children属性,而非自定义的ui参数。这导致Router无法正确获取路由上下文,从而抛出pathname未定义的错误。
修复代码:
export function renderWithProviders( ui, { route = '/', history = createMemoryHistory({initialEntries: [route]}), preloadedState = {}, storeUtils = store(preloadedState), ...renderOptions } = {} ) { // 把Wrapper的参数改为children function Wrapper({ children }) { return ( <Provider store={storeUtils}> <Router history={history}>{children}</Router> </Provider> ); } // 修正返回的store变量名,避免未定义 return { store: storeUtils, ...render(ui, { wrapper: Wrapper, history, ...renderOptions }) }; }
2. 测试用例中的路由路径与实际配置不匹配
实际路由配置是/movies/:id,但测试用例中使用了/api/movies/tt0389790,且Route组件的path写死了具体ID,不符合路由参数的设计逻辑。
修复后的测试用例:
test("data is shown properly", async () => { renderWithProviders( // 直接渲染MovieDetail,路由参数由route指定 <MovieDetail />, { // 匹配实际路由格式 route: '/movies/tt0389790', }, ); // 原组件未渲染poster图片,该行断言会失败,可注释或补充组件代码 // const moviePoster = await screen.findByAltText("Bee Movie"); // expect(moviePoster).toBeInTheDocument(); const movieTitle = await screen.findByText("Bee Movie"); expect(movieTitle).toBeInTheDocument(); const movieYear = await screen.findByText("2007"); expect(movieYear).toBeInTheDocument(); });
3. 额外注意事项
- 原组件未渲染
poster图片,测试用例中对应的断言会失败,需要么在组件中添加图片渲染逻辑,要么注释该行断言。 - 确保Redux store的初始化状态包含
movies.selectedItem的初始值(比如空对象),避免组件渲染时因movie为undefined抛出其他错误。
内容的提问来源于stack exchange,提问作者Elham
相关产品推荐
相关产品推荐

