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

Vitest测试TanStack Router Link组件遇错:如何正确Mock与测试?

Mock TanStack Router Link组件的测试方案

问题背景

父组件中使用@tanstack/react-router的Link组件,测试时用vi.mock()Mock该组件出现Uncaught TypeError: Cannot read properties of null (reading 'buildLocation')错误;手动配置路由通过RouterProvider渲染时,仅加载404路由而非目标Link。需解决:

  1. 如何正确Mock TanStack Router的Link组件;
  2. 如何在Mock环境下测试包含多个Link的大型组件。

1. 正确Mock TanStack Router的Link组件

方式一:浅Mock为普通<a>标签(推荐简单场景)

直接MockLink组件,将其替换为原生<a>标签,传递to属性作为href,完全脱离TanStack Router的内部上下文依赖:

vi.mock('@tanstack/react-router', () => ({
  Link: vi.fn(({ to, children, ...props }) => {
    // 处理字符串和对象两种形式的to参数
    const href = typeof to === 'string' ? to : JSON.stringify(to);
    return <a href={href} {...props}>{children}</a>;
  }),
}));

这种方式无需配置路由上下文,适合仅需验证Link存在和跳转目标的基础测试。

方式二:模拟完整路由上下文(适合需要路由逻辑的场景)

如果需要保留Link的路由参数解析等功能,可手动创建简易路由并通过RouterProvider提供上下文:

import { RouterProvider, createRouter } from '@tanstack/react-router';
import { render } from '@testing-library/react';

// 配置与组件中Link匹配的路由
const testRouter = createRouter({
  routes: [
    { path: '/', element: <div>Home</div> },
    { path: '/about', element: <div>About</div> },
    { path: '/user/:id', element: <div>User</div> }, // 匹配动态路由
  ],
});

// 测试时用RouterProvider包裹目标组件
test('renders Link without 404', () => {
  render(
    <RouterProvider router={testRouter}>
      <YourParentComponent />
    </RouterProvider>
  );
  
  // 验证目标Link存在
  expect(screen.getByRole('link', { name: /user/i })).toBeInTheDocument();
});

注意:若仍加载404路由,需检查路由配置的path是否与Link的to属性完全匹配(包括动态参数格式)。


2. 测试包含多个Link的大型组件

方案一:批量验证Link的跳转目标(基于浅Mock)

利用浅Mock后的<a>标签,通过测试工具批量查询并验证所有Link的属性:

test('all links have correct targets', () => {
  render(<YourLargeComponent />);
  
  // 定义预期的Link映射:显示文本 -> 跳转路径
  const expectedLinks = [
    { text: '首页', href: '/' },
    { text: '关于我们', href: '/about' },
    { text: '用户详情', href: JSON.stringify({ to: '/user/123' }) },
  ];

  // 批量断言
  expectedLinks.forEach(({ text, href }) => {
    const link = screen.getByRole('link', { name: text });
    expect(link).toHaveAttribute('href', href);
  });
});

方案二:验证路由跳转逻辑(基于路由上下文)

若需测试Link点击后的导航行为,可MockuseRouter钩子并断言导航函数的调用:

import { useRouter } from '@tanstack/react-router';

// Mock useRouter钩子,替换navigate方法为模拟函数
vi.mock('@tanstack/react-router', () => ({
  ...vi.importActual('@tanstack/react-router'),
  useRouter: vi.fn(() => ({
    navigate: vi.fn(),
  })),
}));

test('triggers correct navigation on link click', async () => {
  const mockNavigate = vi.fn();
  useRouter.mockReturnValue({ navigate: mockNavigate });

  render(
    <RouterProvider router={testRouter}>
      <YourLargeComponent />
    </RouterProvider>
  );

  // 点击目标Link
  await screen.getByRole('link', { name: /用户详情/i }).click();

  // 断言导航函数被正确调用
  expect(mockNavigate).toHaveBeenCalledWith('/user/123');
});

常见问题排查

  • buildLocation错误:原因是Link在无路由上下文的环境中渲染,选择浅Mock为<a>标签或提供RouterProvider即可解决。
  • 404路由加载:检查路由配置的path与Link的to属性是否完全匹配,动态路由需确保参数定义一致(如/user/:id对应to="/user/123")。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 00:13:16