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

如何用Testing Library测试React Router链接并修复点击不跳转问题

我想要使用Testing Library测试React Router链接,目前已尝试如下实现:

App.js:

import React from 'react'
import { Routes, NavLink, Route } from 'react-router-dom'

function App() {
  return (
    <div>
      <nav>
        <NavLink to={"/"}>Home</NavLink>
        <NavLink to={"/about"}>About</NavLink>
      </nav>
      <Routes>
        <Route path="/" element="home page" />
        <Route path="/about" element="about page" />
      </Routes>
    </div>
  )
}

export default App

App.test.js:

import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { createMemoryHistory } from 'history';
import { Route, Router } from "react-router-dom"
import App from './App';

test('first home link', () => {
  const history = createMemoryHistory({ initialEntries: ["/"] });
  render(
    <Router location={history.location} navigator={history}>
      <App />
    </Router>
  );
  const linkElement = screen.getByRole("link", { name: /about/i });

  userEvent.click(linkElement);
  
  screen.debug();
});

screen.debug输出结果:

<body>
  <div>
    <div>
      <nav>
        <a
          aria-current="page"
          class="active"     
          href="/"
        >
          Home
        </a>
        <a
          class=""
          href="/about"
        >
          About
        </a>
      </nav>
      home page
    </div>
  </div>
</body>

正如screen.debug的输出所示,点击About链接后页面并没有切换到about page,仍然显示home page内容。

请问该如何修复这个路由不跳转的问题?

项目依赖版本:

  • "@testing-library/jest-dom": "^5.16.4"
  • "@testing-library/react": "^13.3.0"
  • "@testing-library/user-event": "^13.5.0"
  • "history": "^5.3.0"
  • "react": "^18.1.0"
  • "react-router-dom": "^6.3.0"
  • "react-dom": "^18.1.0"

修复方案

问题来自两个核心疏漏:

  1. React Router v6 的基础Router组件不会主动监听传入的history实例变化,你直接传入history对象后,路由地址变更时没有触发React状态更新,组件不会重渲染。
  2. @testing-library/user-event的所有交互方法都是异步API,没有加await等待操作执行完成的话,debug/断言逻辑会在路由跳转逻辑跑完之前就执行。

推荐写法(最简便)

测试场景直接用React Router内置的MemoryRouter即可,它内部已经封装了路由状态同步逻辑,不需要手动创建和维护history实例:

import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
// 替换基础Router为MemoryRouter
import { MemoryRouter } from "react-router-dom"
import App from './App';

// 测试用例改为async函数
test('navigate to about page when click about link', async () => {
  const user = userEvent.setup()
  render(
    // 直接通过initialEntries设置初始路由
    <MemoryRouter initialEntries={["/"]}>
      <App />
    </MemoryRouter>
  );

  // 初始状态校验
  expect(screen.getByText("home page")).toBeInTheDocument();
  
  const linkElement = screen.getByRole("link", { name: /about/i });
  // 等待点击交互完成
  await user.click(linkElement);
  
  // 校验跳转结果
  expect(screen.getByText("about page")).toBeInTheDocument();
  screen.debug();
});

自定义history写法

如果必须手动创建history实例使用,需要自己封装路由状态同步逻辑,监听history变化触发组件重渲染:

import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { createMemoryHistory } from 'history';
import { Router } from "react-router-dom"
import { useState, useLayoutEffect } from 'react';
import App from './App';

// 封装支持自定义history的路由组件
function HistoryRouter({history, ...props}) {
  const [routeState, setRouteState] = useState({
    action: history.action,
    location: history.location
  })

  useLayoutEffect(() => {
    // 监听路由变化,同步到React状态触发重渲染
    const unlisten = history.listen(setRouteState)
    return unlisten
  }, [history])

  return <Router 
    {...props} 
    location={routeState.location} 
    navigationType={routeState.action} 
    navigator={history} 
  />
}

test('navigate to about page when click about link', async () => {
  const history = createMemoryHistory({ initialEntries: ["/"] });
  const user = userEvent.setup()
  render(
    <HistoryRouter history={history}>
      <App />
    </HistoryRouter>
  );

  expect(screen.getByText("home page")).toBeInTheDocument();
  const linkElement = screen.getByRole("link", { name: /about/i });
  await user.click(linkElement);
  expect(screen.getByText("about page")).toBeInTheDocument();
});

注:React Router v6.4以上版本已经内置了HistoryRouter组件,不需要手动封装,但你当前使用的6.3.0版本无此内置能力,测试场景优先选择MemoryRouter成本最低。


内容的提问来源于stack exchange,提问作者Mehdi Saa

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.31 00:51:24