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

如何为含useEffect、axios、useParams的React组件编写单元测试?

MovieDetails组件单元测试实现方案

1. 正确传递路由参数给useParams

测试时useParams拿不到movieId的核心原因是MemoryRouter未配置正确的初始路由条目。你需要在渲染组件时,通过initialEntries传入包含目标movieId的路由路径,确保useParams能正确解析参数:

import { MemoryRouter } from 'react-router-dom';
import { render, screen } from '@testing-library/react';
import MovieDetails from './MovieDetails';

test('组件挂载时能获取到正确的movieId', () => {
  render(
    <MemoryRouter initialEntries={['/movies/123']}>
      <MovieDetails />
    </MemoryRouter>
  );
  // 后续可结合请求mock验证逻辑是否使用了正确的movieId
});

注意:initialEntries里的路径必须和组件实际绑定的路由规则一致(比如组件路由是/movies/:movieId,就必须传/movies/xxx格式的路径)。

2. Mock Axios 避免真实接口请求

直接调用真实接口会导致测试不稳定,用Jest mock掉axios,同时模拟请求的成功、失败、挂起等场景:

模拟请求成功场景

import axios from 'axios';

// 全局mock axios
jest.mock('axios');

test('成功获取电影详情并渲染', async () => {
  // 模拟接口返回数据
  const mockMovie = { id: 123, title: '星际穿越', overview: '未来地球资源枯竭...' };
  axios.get.mockResolvedValue({ data: mockMovie });

  render(
    <MemoryRouter initialEntries={['/movies/123']}>
      <MovieDetails />
    </MemoryRouter>
  );

  // 断言加载状态显示
  expect(screen.getByText('加载中...')).toBeInTheDocument();
  // 等待请求完成,断言电影标题渲染
  const title = await screen.findByText('星际穿越');
  expect(title).toBeInTheDocument();
  // 验证axios请求的URL和参数正确
  expect(axios.get).toHaveBeenCalledWith('/api/movies/123', expect.any(Object));
});

3. 测试请求取消与卸载清理逻辑

组件里用了axios的CancelToken处理卸载时的请求取消,测试时需要mockCancelToken.source来捕获取消行为:

test('组件卸载时取消未完成的请求', async () => {
  // 模拟CancelToken.source返回的对象,捕获cancel方法调用
  const mockCancelFn = jest.fn();
  axios.CancelToken.source.mockReturnValue({
    token: 'mock-token',
    cancel: mockCancelFn
  });
  // 让请求处于挂起状态,避免自动完成
  axios.get.mockImplementation(() => new Promise(() => {}));

  // 渲染组件并获取卸载方法
  const { unmount } = render(
    <MemoryRouter initialEntries={['/movies/123']}>
      <MovieDetails />
    </MemoryRouter>
  );

  // 触发组件卸载
  unmount();

  // 断言cancel方法被调用,且传入了正确的取消信息
  expect(mockCancelFn).toHaveBeenCalledWith('Component unmounted');
});

4. 测试错误处理场景

模拟请求失败(非取消错误)

test('请求失败时显示错误提示', async () => {
  axios.get.mockRejectedValue(new Error('接口请求失败'));

  render(
    <MemoryRouter initialEntries={['/movies/123']}>
      <MovieDetails />
    </MemoryRouter>
  );

  // 断言错误提示渲染
  const errorText = await screen.findByText('加载电影详情失败,请稍后重试');
  expect(errorText).toBeInTheDocument();
});

模拟请求被取消的场景

确保组件不会把取消请求的错误当成普通错误展示:

test('请求被取消时不显示错误提示', async () => {
  // 创建axios取消错误实例
  const cancelError = axios.Cancel('组件已卸载');
  axios.get.mockRejectedValue(cancelError);

  render(
    <MemoryRouter initialEntries={['/movies/123']}>
      <MovieDetails />
    </MemoryRouter>
  );

  // 断言错误提示不会出现
  await waitFor(() => {
    expect(screen.queryByText('加载电影详情失败,请稍后重试')).not.toBeInTheDocument();
  });
});

内容的提问来源于stack exchange,提问作者burak mert intepe

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 02:58:26