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

React 18 + Jest 29下自定义Hook手动Mock失效求助

自定义Hook Mock失败的问题排查与修复

问题背景

使用React 18和Jest 29测试SCMovieDetail组件时,尝试通过__mocks__目录复用useFetch自定义Hook的Mock实现,但测试始终调用真实Hook,无法触发Mock逻辑。

错误原因分析

  • jest.mock调用时机错误:测试用例内部调用jest.mock时,组件已完成导入,Mock无法覆盖真实模块
  • __mocks__文件命名不匹配:src/hooks/__mocks__下的文件为useFetch.tsx,但原Hook文件是useFetch.ts,Jest无法正确识别匹配的Mock文件
  • useParams未Mock:测试中useParams返回undefined,导致useFetch的URL为http://localhost:3000/movies/undefined,无法命中Mock预设的分支
  • 断言逻辑不合理:测试中断言'Hello!'存在,但组件渲染的SCServerError显示的是useFetch返回的error值,两者不匹配

修复步骤

1. 调整jest.mock调用位置

将jest.mock移到测试文件顶部,确保在组件导入前生效:

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

// 先Mock依赖,再导入组件
jest.mock('../../hooks/useFetch');
jest.mock('react-router-dom', () => ({
  ...jest.requireActual('react-router-dom'),
  useParams: () => ({ slug: 'error' })
}));

import { SCMovieDetail } from './MovieDetail';

2. 修正__mocks__文件命名

将src/hooks/__mocks__/useFetch.tsx重命名为useFetch.ts,与原Hook文件扩展名保持一致,确保Jest能正确识别Mock模块。

3. 适配Mock的URL判断逻辑

修改useFetch的Mock实现,让它能根据实际请求URL的特征返回对应状态(比如URL包含error/loading关键词):

// src/hooks/__mocks__/useFetch.ts
import { useFetchPayload, useFetchProps } from '../useFetch';

export const useFetch = <T>({ url, method = 'GET' }: useFetchProps): useFetchPayload<T> => {
  let data: T | undefined = undefined;
  let error: string | undefined = undefined;
  let loading = false;

  if (url.includes('error')) {
    error = `${method} request failed`;
  } else if (url.includes('loading')) {
    loading = true;
  } else if (url.includes('success')) {
    data = {} as T;
  }

  return { data, error, loading };
};

4. 修正断言逻辑

匹配Mock返回的错误信息,调整断言内容:

it('Should render error', () => {
  render(componentJSX());
  expect(screen.getByText('GET request failed')).toBeInTheDocument();
});

完整测试代码示例

// src/components/movies/MovieDetail.test.tsx
import renderer from 'react-test-renderer';
import { render, screen } from '@testing-library/react';
import { MemoryRouter } from 'react-router-dom';

// 提前Mock依赖
jest.mock('../../hooks/useFetch');
jest.mock('react-router-dom', () => ({
  ...jest.requireActual('react-router-dom'),
  useParams: () => ({ slug: 'error' })
}));

import { SCMovieDetail } from './MovieDetail';

describe('SCMovieDetail', () => {
  const componentJSX = () => {
    return (
      <MemoryRouter>
        <SCMovieDetail />
      </MemoryRouter>
    );
  };

  it('Should match snapshot', () => {
    const component = renderer.create(componentJSX());
    const tree = component.toJSON();
    expect(tree).toMatchSnapshot();
  });

  it('Should render error', () => {
    render(componentJSX());
    expect(screen.getByText('GET request failed')).toBeInTheDocument();
  });
});

修正后的目录结构

src/
|-- ...
|-- components/
|   | ...
|   |-- movies/
|   |   |-- MovieDetail.tsx
|   |   |-- MovieDetail.test.tsx 
|   |   |-- ...
|-- hooks/
|   |-- __mocks__/
|   |   |-- useFetch.ts  # 修正扩展名
|   |-- useFetch.ts
|   |-- ...
|-- ...

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 20:44:52