如何使用React Testing Library测试React Router v6中<Link>组件的点击行为?
测试react-router-dom v6中组件点击行为失败的问题
我尝试测试用户点击react-router-dom v6里的组件的行为,但多种方法都无效,主要问题是代码检查工具提示点击事件未执行、location没有发生改变。
主文件代码
ReactDOM.render( <React.StrictMode> <Provider store={store}> <BrowserRouter> <LangContextProvider> <App /> </LangContextProvider> </BrowserRouter> </Provider> </React.StrictMode>, document.getElementById('root') );
路由配置
<Routes> <Route path="/" element={<MainLayout />}> {/* TODO 重构此处路由 */} <Route index element={<MainPage />} /> <Route path="main" element={<MainPage />}> <Route path="auth" element={<AuthFormComponent />} /> <Route path="page-not-found" element={<NotFoundPage />} /> <Route path="*" element={<Navigate to="page-not-found" replace />} /> </Route> .... </Routes>
待测试组件
const FooterComponent = () => { return ( <div className={cn(style.container)} data-testid="footer-component"> <div className={cn(style.upper)}> <div className={style.svgContainer}> <CloverSvg /> </div> <div className={cn(style.tutotarial)}> <ul> <li><h3>开始</h3></li> <li onClick={() => scrollToElementIdHandler("hero")}>开始使用</li> <li onClick={() => scrollToElementIdHandler("hero")}>移动应用</li> </ul> </div> <div className={cn(style.links)}> <ul> <li><h3>关于项目</h3></li> <li><Link to="/faq">FAQ</Link></li> <li> <Link to="/term-of-use">用户协议</Link> </li> <li> <Link to="/privacy-policy" data-testid="privacy-policy-link">隐私政策</Link> </li> </ul> </div> </div> <div className={cn(style.bottom)}> <ul> <li>首页</li> <li>功能特性</li> <li>图库</li> </ul> <ul> <li>联系我们</li> <li>开发说明</li> <li>用户评价</li> </ul> <div className={style.social}> <div><i className="fab fa-telegram"></i></div> <div><i className="fab fa-vk"></i></div> <div><i className="fab fa-instagram"></i></div> <div><i className="fab fa-facebook"></i></div> <div><i className="fab fa-blogger"></i></div> </div> <div className={cn(style.time)}> <p>© 2021–{new Date().getFullYear()}, Memorush, 官方网站</p> </div> </div> </div> ) } export default FooterComponent;
我的测试代码1
it("should change current location", async () => { const history = createBrowserHistory(); // 模拟push方法 history.push = jest.fn(); render(<BrowserRouter><FooterComponent/></BrowserRouter>); await fireEvent.click(screen.getByTestId(/privacy-policy-link/i)); expect(history.location.pathname).toBe(/privacy-policy/i); });
我的测试代码2
it("should change current location", async () => { const history = createMemoryHistory(); // 模拟push方法 history.push = jest.fn(); render(<BrowserRouter><FooterComponent/></BrowserRouter>); await fireEvent.click(screen.getByTestId(/privacy-policy-link/i)); expect(history.push).toHaveBeenCalledWith(/privacy-policy/i); });
问题根源
你的测试代码存在两个核心错误:
- 手动创建的
history实例未被<BrowserRouter>使用,组件内的<Link>会调用BrowserRouter自身创建的history对象,和你定义的实例无关。 - 用正则表达式搭配
toBe断言路径是错误的——toBe要求严格相等,路径字符串需和字符串直接对比,或用toMatch匹配正则。
正确测试方案
方案1:用createMemoryRouter+RouterProvider(推荐)
react-router-dom v6推荐用RouterProvider配合createMemoryRouter模拟路由环境,更贴近真实场景:
import { RouterProvider, createMemoryRouter } from 'react-router-dom'; import { render, screen, fireEvent } from '@testing-library/react'; import FooterComponent from './FooterComponent'; import NotFoundPage from './NotFoundPage'; it('点击链接后跳转到隐私政策页面', () => { // 配置测试所需路由 const router = createMemoryRouter([ { path: '/', element: <FooterComponent />, }, { path: '/privacy-policy', element: <div>隐私政策</div>, }, { path: '*', element: <NotFoundPage />, }, ]); render(<RouterProvider router={router} />); // 触发点击 fireEvent.click(screen.getByTestId('privacy-policy-link')); // 验证路径是否正确 expect(router.state.location.pathname).toBe('/privacy-policy'); });
方案2:验证目标页面渲染
除了检查路径,还可以验证目标页面是否渲染,确保跳转逻辑完整:
import { RouterProvider, createMemoryRouter } from 'react-router-dom'; import { render, screen, fireEvent } from '@testing-library/react'; import FooterComponent from './FooterComponent'; it('点击隐私政策链接后显示对应页面', () => { const router = createMemoryRouter([ { path: '/', element: <FooterComponent />, }, { path: '/privacy-policy', element: <div data-testid="privacy-page">隐私政策内容</div>, }, ]); render(<RouterProvider router={router} />); fireEvent.click(screen.getByTestId('privacy-policy-link')); // 验证目标页面元素存在 expect(screen.getByTestId('privacy-page')).toBeInTheDocument(); });
方案3:用BrowserRouter配合window.location(不推荐)
若需模拟浏览器环境,可直接检查window.location,但这种方法依赖浏览器API,不如MemoryRouter可靠:
import { BrowserRouter, Routes, Route } from 'react-router-dom'; import { render, screen, fireEvent } from '@testing-library/react'; import FooterComponent from './FooterComponent'; it('点击链接后更新window.location', () => { render( <BrowserRouter> <FooterComponent /> <Routes> <Route path="/privacy-policy" element={<div>隐私政策</div>} /> </Routes> </BrowserRouter> ); fireEvent.click(screen.getByTestId('privacy-policy-link')); expect(window.location.pathname).toBe('/privacy-policy'); });
注意事项
- 测试时要配置完整的路由,否则会触发路由配置里的
*重定向到404页面,导致跳转验证失败。 - 不要手动创建history实例却不传递给Router组件,
<BrowserRouter>和<MemoryRouter>默认会生成自己的history实例。 - 使用
fireEvent.click无需await,如果用userEvent.click则需要加await,因为userEvent会模拟真实用户的交互流程。
内容的提问来源于stack exchange,提问作者Yuri
相关产品推荐
相关产品推荐

