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

React Testing Library测试自定义Hook中Mock API调用失败求助

自定义Hook useGetUsers测试失败:无法进入API调用的.then()分支

我编写了一个名为useGetUsers的自定义Hook,在其useEffect函数中调用API并将结果存入状态,代码如下:

export const useGetUsers = (searchParams?: any | undefined, userId?: string) => {
  const [users, setUsers] = useState<{
    data: readonly any[] | null;
    loading: boolean;
  }>({
    data: [],
    loading: true,
  });

  const parsedSearchParams = {
    limit: 100,
    ...(searchParams || {}),
  };
  const searchParamStr = `?${makeQueryStringFromObject(parsedSearchParams)}`;

  useEffect(() => {
    userRequest('users', 'get', null, searchParamStr)
      .then(result => {
        setUsers({
          data: result?.data,
          loading: false,
        });
      })
      .catch(() => {
        setUsers({
          data: null,
          loading: false,
        });
      });
  }, [userId, searchParamStr]);

  return { users, setUsers };
};

我希望测试能进入API调用的.then()分支,但实际却进入了.catch()分支。尝试了两种测试写法均未成功,也试过waitForNextUpdate,依然无效:

第一种测试代码

test('when the call is a success', async () => {
    const spy = jest.spyOn(ES, 'userRequest');
    const returnPromise = Promise.resolve({data: ['a']})
    ES.userRequest = jest.fn(() => returnPromise);

    const { result, waitFor} = renderHook(() => useGetUsers());
    await act(() => returnPromise)

    await waitFor(() => expect(spy).toHaveBeenCalled())//this fails
    
});

第二种测试代码

test('when the call is a success', async () => {
    jest.mock('src/..', () => ({
        ...jest.requireActual('src/..'),
        userRequest: jest
            .fn()
            .mockImplementation(() => new Promise(resolve => resolve({data: ['a']}))),
    }));
    const { result, waitFor} = renderHook(() => useGetUsers());
    await waitFor(() => expect(ES.userRequest).toHaveBeenCalled())
});

问题修复方案

1. 正确Mock userRequest 模块

jest.mock必须放在测试文件的顶部作用域(不能放在单个test函数内部),否则无法在模块加载前完成替换:

// 放在测试文件最顶部,替换成userRequest实际所在的路径
jest.mock('src/path/to/userRequestModule', () => ({
  ...jest.requireActual('src/path/to/userRequestModule'),
  userRequest: jest.fn().mockResolvedValue({ data: ['a'] })
}));

import { userRequest } from 'src/path/to/userRequestModule';
import { useGetUsers } from './path/to/useGetUsers';
import { renderHook, waitFor } from '@testing-library/react';

2. 修复Spy写法(第一种测试的修正版)

第一种测试中,直接覆盖ES.userRequest会导致之前创建的spy失效,改用mockImplementation替换原有实现:

test('when the call is a success', async () => {
  const mockResponse = Promise.resolve({ data: ['a'] });
  // 用mockImplementation替换实现,保留spy引用
  jest.spyOn(ES, 'userRequest').mockImplementation(() => mockResponse);

  const { result } = renderHook(() => useGetUsers());
  
  // 等待状态更新(loading变为false),而非直接执行promise
  await waitFor(() => {
    expect(result.current.users.loading).toBe(false);
    expect(result.current.users.data).toEqual(['a']);
  });

  // 验证API调用参数
  expect(ES.userRequest).toHaveBeenCalledWith('users', 'get', null, expect.any(String));
});

3. 关键排查点

  • 检查makeQueryStringFromObject函数是否在测试环境中正常工作:如果该函数抛出错误,会直接导致useEffect中的Promise进入catch分支
  • 优先通过状态变化(如loading: false)来验证测试结果,而非仅检查函数是否被调用,这样能覆盖更多执行路径
  • 确保所有依赖模块的Mock在测试执行前完成,避免模块加载顺序导致Mock失效

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.12 18:45:47