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

如何使用Jest和RTL测试React应用路由导航功能

路由跳转阻塞问题排查思路
  • 首先排查历史对象匹配错误:你在测试中创建了createMemoryHistory注入Router组件,但所有路径断言用的是全局window.location.pathname,createMemoryHistory是内存型历史对象,默认不会同步浏览器window.location属性,前序测试能通过属于巧合,需将所有路径断言替换为校验你自行创建的history对象的location.pathname属性。
  • 其次排查异步调用规则错误:userEvent v13及以上版本的所有交互方法均为异步方法,必须加await等待执行完成,你在调用click、type方法时均未加await,会导致状态更新时序异常;同时多余的act包裹会阻塞React的状态更新逻辑,点击a标签的逻辑不需要额外包裹act,直接移除外层的await act(async () => {})即可。
  • 排查跳转拦截逻辑:确认项目中是否存在全局路由守卫、权限校验逻辑,是否/dashboard/31929路由的权限校验未通过,导致跳转被拦截回/installations,可在点击后打印history.location.pathname确认是未触发跳转还是跳转后被回退。
  • 排查路由配置错误:确认路由表中已正确注册/dashboard/:installationId路径,无拼写错误,且该路由的注册顺序在通配符路由之前,不会被优先级更高的路由提前匹配拦截。

修复后测试代码参考

it( 'Full app rendering/navigating', async () => {
  const history = createMemoryHistory()
  render(
    <Router history={history}>
      <App />
    </Router>,
  )
  // 校验初始路径
  await waitFor(() => expect(history.location.pathname).toBe('/'))

  const email = process.env.TEST_EMAIL
  const password = process.env.TEST_PASSWORD

  // 首页登录按钮
  const signInButton = screen.getByRole('button', {name: 'Sign in' })
  expect(signInButton).toBeInTheDocument()

  // 点击跳转登录页
  await userEvent.click(signInButton, {button: 0})
  await waitFor(() => expect(history.location.pathname).toBe('/signin'))

  // 登录页元素校验
  const emailField = screen.getByPlaceholderText('Email address')
  const passwordField = screen.getByPlaceholderText('Your password')
  expect(emailField).toBeInTheDocument()
  expect(passwordField).toBeInTheDocument()

  // 输入账号密码
  await userEvent.type(emailField, email)
  await userEvent.type(passwordField, password)

  const loginButton = screen.getByRole('button', { name: 'Sign in' })
  expect(loginButton).toBeInTheDocument()

  // 触发登录
  await userEvent.click(loginButton)
  await waitFor(() => expect(loginButton).not.toBeInTheDocument())

  // 安装选择页校验
  const title = screen.getByText('Installation selection')
  expect(title).toBeInTheDocument()

  const infoText = screen.getByText('Please, wait a little bit while we are fetching {{data}}')
  expect(infoText).toBeInTheDocument()
  // 等待列表加载完成
  await waitFor(() => expect(infoText).not.toBeInTheDocument())

  // 点击跳转动态路由
  const maisonPassive = screen.getByText('Maison passive')
  expect(maisonPassive).toBeInTheDocument()
  const maisonPassiveButton = maisonPassive.closest('a')
  expect(maisonPassiveButton).toHaveAttribute('href', '/dashboard/31929')
  await userEvent.click(maisonPassiveButton, { button: 0 })

  // 校验路径跳转成功
  await waitFor (() => expect(history.location.pathname).toBe('/dashboard/31929'))
})

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.26 04:24:07