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

测试React Router组件时遇useLoaderData报错问题求助

问题:Vitest同时运行两个React Router组件测试时抛出useLoaderData must be used within a data router错误
  • 单独运行任意一个测试均正常通过,同时运行则触发上述错误
  • 涉及组件:Timestamp(依赖useLoaderData)、DateError(依赖useRouteError)
  • 生产环境使用React Router v6+的createBrowserRouter(数据路由),测试中用了MemoryRouter包裹组件

错误原因

  1. vi.mock使用方式错误:将vi.mock写在测试函数内部,Vitest的模块缓存机制会导致两个测试的mock冲突,后运行的测试无法正确覆盖前一个的mock配置。
  2. 测试路由环境不匹配:useLoaderData、useRouteError是数据路由专属钩子,仅能在RouterProvider(而非传统的MemoryRouter)提供的上下文内使用,即使mock了钩子函数,React Router的内部校验仍会检测到环境不匹配并报错。

解决步骤及修改后的代码

1. 调整mock策略

将react-router-dom的mock移到测试文件顶部,全局统一mock,再在每个测试中单独设置mock返回值,避免缓存冲突。

2. 替换测试路由环境

用createMemoryRouter创建测试用数据路由,通过RouterProvider提供上下文,匹配生产环境的路由模式。

修改后的完整测试代码

import { cleanup, render, screen } from '@testing-library/react';
import { vi } from 'vitest';
import { createMemoryRouter, RouterProvider } from 'react-router-dom';
import Timestamp from './Timestamp';
import DateError from './DateError';

// 全局mock react-router-dom,只mock需要的钩子
vi.mock("react-router-dom", async () => {
  const originalModule = await vi.importActual("react-router-dom");
  return {
    ...originalModule,
    useLoaderData: vi.fn(),
    useRouteError: vi.fn(),
  }
});

// 每个测试后清理React上下文并重置mock
afterEach(() => {
  cleanup();
  vi.clearAllMocks();
});

test('Verify that <Timestamp /> displays both the UNIX and UTC dates', () => {
  // 设置当前测试的useLoaderData返回值
  const mockDateData = {unix: 7728271946, utc: "Tue, 31 Mar 1970 10:44:31 GMT"};
  vi.mocked(vi.importActual("react-router-dom").useLoaderData).mockReturnValue(mockDateData);

  // 创建测试用内存数据路由
  const router = createMemoryRouter([
    {
      path: "/:date",
      element: <Timestamp />,
    },
  ], {
    initialEntries: ['/123456'], // 传入合法的date参数占位
  });

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

  const timestamps = screen.getAllByRole("paragraph");
  expect(timestamps[0]).toHaveTextContent("Date in UNIX: 7728271946");
  expect(timestamps[1]).toHaveTextContent("Date in UTC: Tue, 31 Mar 1970 10:44:31 GMT");
});

test('Verify that DateError displays invalid date message', () => {
  // 设置当前测试的useRouteError返回值
  const mockErrorMessage = "282347223489234 is an invalid date!";
  vi.mocked(vi.importActual("react-router-dom").useRouteError).mockReturnValue(mockErrorMessage);

  // 创建包含errorElement的测试路由,模拟loader抛出错误
  const router = createMemoryRouter([
    {
      path: "/:date",
      element: <Timestamp />,
      errorElement: <DateError />,
      loader: () => { throw new Error(mockErrorMessage); },
    },
  ], {
    initialEntries: ['/282347223489234'],
  });

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

  const message = screen.getByRole("paragraph");
  expect(message).toHaveTextContent("282347223489234 is an invalid date!");
});

关键改动说明

  • 全局mock+测试内重置:避免多次mock导致的模块缓存问题,确保每个测试的mock独立。
  • 使用RouterProvider:匹配生产环境的 data router 上下文,解决钩子的环境校验问题。
  • 模拟真实路由结构:用createMemoryRouter配置测试路由,能正确触发loader和errorElement的逻辑,测试更贴近真实场景。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 18:35:17