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

如何用Jest测试React-Router-DOM v6中含Outlet的组件?

解决Jest测试React-Router-DOM Outlet组件时子路由组件不渲染的问题

你在测试嵌套路由的Preferences组件时,直接挂载<Preferences>只会渲染导航栏,Outlet无法渲染Account-1这类子组件,核心原因是测试环境未提供React Router的路由上下文,Outlet无法识别当前匹配的子路由。以下是几种可行的解决方式:

方法1:在测试中模拟完整路由结构

直接在测试里用<Routes>和<Route>包裹Preferences,指定要渲染的子路由,让Outlet能正确加载对应子组件:

import { Routes, Route } from 'react-router-dom';
import Account1 from './path-to-Account1';
import LazyAccount from './path-to-LazyAccount';

describe('Preferences screen with Account', () => {
  beforeEach(cleanup);

  afterEach(() => {
    jest.clearAllMocks();
  });

  test('渲染Preferences并显示Account-1子组件', async () => {
    // 模拟完整路由结构,设置初始匹配的路由地址
    mountWithApp(
      <Routes>
        <Route path="/preferences" element={<Preferences />}>
          <Route index element={<LazyAccount />} />
          <Route path="accounts-1" element={<Account1 />} />
        </Route>
      </Routes>,
      { initialEntries: ['/preferences/accounts-1'] }
    );

    // 验证子组件内容渲染
    expect(screen.getByText('Account-1相关内容')).toBeInTheDocument();

    // 测试导航逻辑
    await userEvent.click(screen.getByText('ACCOUNT'));
    expect(mockedUseNavigate).toHaveBeenCalledWith('/preferences/accounts');
  });
});

注意:如果mountWithApp已包含<MemoryRouter>或<BrowserRouter>,只需补充Routes和Route结构;若未包含,需在最外层包裹<MemoryRouter>并指定initialEntries。

方法2:Mock Outlet组件,手动传入子组件

若仅需测试Preferences的导航逻辑,无需真实渲染子组件,可直接Mock Outlet并替换为目标子组件:

import { Outlet } from 'react-router-dom';
import Account1 from './path-to-Account1';

jest.mock('react-router-dom', () => ({
  ...jest.requireActual('react-router-dom'),
  Outlet: jest.fn(() => <Account1 />),
}));

describe('Preferences screen with Account', () => {
  beforeEach(cleanup);

  afterEach(() => {
    jest.clearAllMocks();
  });

  test('点击Account选项触发导航', async () => {
    mountWithApp(<Preferences />);

    expect(screen.getByText('ACCOUNT')).toBeInTheDocument();
    // 验证Mock的子组件是否渲染
    expect(screen.getByText('Account-1相关内容')).toBeInTheDocument();

    await userEvent.click(screen.getByText('ACCOUNT'));
    expect(mockedUseNavigate).toHaveBeenCalledWith('/preferences/accounts');
  });
});

这种方式适合快速测试父组件交互逻辑,无需依赖完整路由配置。

方法3:验证路由跳转后的子组件切换(集成测试)

若要测试导航后子组件的切换逻辑,可结合initialEntries和路由切换后的断言:

test('导航到accounts-1后切换对应子组件', async () => {
  mountWithApp(
    <Routes>
      <Route path="/preferences" element={<Preferences />}>
        <Route index element={<LazyAccount />} />
        <Route path="accounts-1" element={<Account1 />} />
      </Route>
    </Routes>,
    { initialEntries: ['/preferences'] }
  );

  // 初始显示默认子组件
  expect(screen.getByText('默认Account内容')).toBeInTheDocument();

  // 点击导航到accounts-1
  await userEvent.click(screen.getByText('ACCOUNTS-1'));
  
  // 验证子组件切换
  expect(screen.queryByText('默认Account内容')).not.toBeInTheDocument();
  expect(screen.getByText('Account-1相关内容')).toBeInTheDocument();
});

关键注意点

  • 测试中的路由配置需与项目实际结构一致,避免测试与生产行为不符
  • 若使用懒加载组件(LazyComponent),测试时可直接导入真实组件,或Mock懒加载逻辑
  • 确认mountWithApp已提供React Router上下文(如包含<MemoryRouter>)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 14:33:19