JavaScript路由跳转测试需隔离真实history,用jest.mock模拟window.history或React Router的useNavigate;原生API测试重置mock并断言参数,React Router测试须用MemoryRouter或createMemoryRouter提供上下文并验证location。

在 JavaScript 中测试依赖浏览器历史记录(如 history.pushState、history.replaceState 或 React Router 的 useNavigate)的路由跳转函数,关键在于**隔离真实浏览器 API,用可控制的模拟对象替代 history,并验证跳转行为是否符合预期**。不直接操作真实 history,否则测试会污染全局状态、不可靠且难以断言。
使用 jest.mock 模拟 window.history
对于原生 history 操作(如封装的 goToPage 函数),可在测试前 mock window.history 的方法:
- 用
jest.fn()替换pushState和replaceState,记录调用参数 - 手动设置
window.location.href初始值,便于后续断言 - 注意:需在
beforeEach中重置 mock,避免测试间干扰
示例:
// utils/router.js
export function navigateTo(path) {
history.pushState(null, '', path);
}
// __tests__/router.test.js
test('navigateTo calls history.pushState with correct path', () => {
const mockPushState = jest.fn();
delete window.history;
window.history = { pushState: mockPushState };
navigateTo('/dashboard');
expect(mockPushState).toHaveBeenCalledWith(null, '', '/dashboard');
});
测试 React Router v6+ 的 useNavigate
React Router 的导航函数必须在 Router 上下文中运行。测试时需用 MemoryRouter(或 createMemoryRouter + RouterProvider)包裹组件,并通过 render 提供路由环境:
立即学习“Java免费学习笔记(深入)”;
Java项目代码review工具。分析Git变更+完整调用链路上下文,推断业务需求,进行多维度评分和分类汇总,生成完整PRD文档。包含细粒度Java代码审查清单(Null安全、异常处理、Streams、并发、equals/hashCode、资源管理、API设计、性能、MyBatis/ORM、事务边界、SQL/DD...
- 使用
@testing-library/react的render配合MemoryRouter - 若测试自定义 hook,可用
renderHook+Wrapper注入 Router - 触发跳转后,用
screen.getByRole或waitFor验证目标组件是否渲染,或检查useLocation().pathname
示例(测试 hook):
// hooks/useCustomNavigate.js
import { useNavigate } from 'react-router-dom';
export function useCustomNavigate() {
const navigate = useNavigate();
return (path) => navigate(path, { replace: true });
}
// __tests__/useCustomNavigate.test.js
import { renderHook, act } from '@testing-library/react';
import { MemoryRouter } from 'react-router-dom';
import { useCustomNavigate } from '../hooks/useCustomNavigate';
test('useCustomNavigate calls navigate with replace: true', () => {
const wrapper = ({ children }) => (
<MemoryRouter initialEntries={['/home']}>{children}</MemoryRouter>
);
const { result } = renderHook(() => useCustomNavigate(), { wrapper });
act(() => {
result.current('/profile');
});
// 可通过 router state 断言,或结合 useLocation 测试
});
用 createMemoryRouter 精确控制路由状态(推荐)
React Router v6.4+ 推荐使用 createMemoryRouter + RouterProvider,它提供更可控的初始路由、loader/data 行为,也便于断言跳转后的 location:
- 构造内存路由实例,指定
initialEntries和initialIndex - 用
jest.spyOn监听router.navigate调用,或直接检查router.state.location.pathname - 适合集成测试导航逻辑与数据加载的组合场景
示例:
import { createMemoryRouter, RouterProvider } from 'react-router-dom';
import { render } from '@testing-library/react';
test('clicking link navigates to /about', () => {
const router = createMemoryRouter(
[
{ path: '/', element: <div>Home</div> },
{ path: '/about', element: <div>About</div> },
],
{ initialEntries: ['/'] }
);
const { container } = render(<RouterProvider router={router} />);
// 触发跳转(如点击按钮)
fireEvent.click(screen.getByText('Go to About'));
// 断言路由已变更
expect(router.state.location.pathname).toBe('/about');
});
避免常见陷阱
测试路由跳转时容易踩坑,需注意:
- 不要在测试中调用
jest.resetModules()后忘记重新导入被测模块,否则 mock 不生效 - 真实
history.pushState不会触发页面刷新或popstate事件,mock 时无需模拟事件派发,除非你显式监听了它 - 若函数内部用了
window.location.assign,也要单独 mockwindow.location对象(包括assign和hrefgetter/setter) - 跨测试文件共享 mock 时,用
jest.mock自动 mock 更安全,而非手动赋值window.history


















