如何使用Jest和RTL测试React应用路由导航功能
路由跳转阻塞问题排查思路
- 首先排查历史对象匹配错误:你在测试中创建了
createMemoryHistory注入Router组件,但所有路径断言用的是全局window.location.pathname,createMemoryHistory是内存型历史对象,默认不会同步浏览器window.location属性,前序测试能通过属于巧合,需将所有路径断言替换为校验你自行创建的history对象的location.pathname属性。 - 其次排查异步调用规则错误:
userEventv13及以上版本的所有交互方法均为异步方法,必须加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
相关产品推荐
相关产品推荐

