You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用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);
});

问题根源

你的测试代码存在两个核心错误:

  1. 手动创建的history实例未被<BrowserRouter>使用,组件内的<Link>会调用BrowserRouter自身创建的history对象,和你定义的实例无关。
  2. 用正则表达式搭配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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.24 01:32:02