
本文介绍如何在 Jest 测试中正确模拟自定义 Hook(如 useGetRequest),并通过 jest.fn() 捕获调用行为、验证执行次数及访问返回值,解决无法直接断言 mock 内部逻辑的问题。
本文介绍如何在 jest 测试中正确模拟自定义 hook(如 `usegetrequest`),并通过 `jest.fn()` 捕获调用行为、验证执行次数及访问返回值,解决无法直接断言 mock 内部逻辑的问题。
在使用 jest.mock() 模拟模块时,一个常见误区是:直接在工厂函数中返回一个普通对象或内联函数(如 () => ({ data, isLoading })),这会导致该函数无法被 Jest 追踪调用——因为 jest.mock 本身不自动包装函数为 mock 实例,toHaveBeenCalled() 等断言将始终失败。
✅ 正确做法是:将待测试的导出函数显式声明为 jest.fn(),再将其注入 mock 模块。这样既能控制返回值,又能利用 Jest 的 mock 跟踪能力。
以下是一个完整、可运行的示例:
// 假设被测组件使用了 ../../../api 中的 useGetRequest
import { render } from '@testing-library/react';
import React from 'react';
import { useGetRequest } from '../../../api'; // 原始导入路径
// 1. 提前定义可追踪的 mock 函数
const mockMyCards = [{ id: 1, name: 'Card A' }];
const mockedUseGetRequest = jest.fn(() => ({
data: mockMyCards,
isLoading: false,
}));
// 2. 使用 jest.mock 模拟整个模块,并返回该 mock 函数
jest.mock('../../../api', () => ({
useGetRequest: mockedUseGetRequest,
}));
// 3. 编写测试用例
describe('YourComponent', () => {
afterEach(() => {
mockedUseGetRequest.mockClear(); // 清理调用记录,避免测试间干扰
});
it('should call useGetRequest exactly once on render', () => {
render(<YourComponent />);
expect(mockedUseGetRequest).toHaveBeenCalledTimes(1);
expect(mockedUseGetRequest).toHaveBeenCalledWith(); // 可选:验证调用参数(若 hook 接收参数)
// 还可进一步校验返回值结构(非必需,但增强健壮性)
const result = mockedUseGetRequest();
expect(result.data).toEqual(mockMyCards);
expect(result.isLoading).toBe(false);
});
});⚠️ 注意事项:
- 不要在 jest.mock 工厂函数内部直接调用 jest.fn()(如 () => jest.fn(...)),否则每次导入都会创建新实例,导致外部引用失效;
- mock 必须在 describe 外部或 beforeAll 中定义,确保测试前已生效;
- 若 useGetRequest 接收参数(如 useGetRequest(url)),需在 mockedUseGetRequest.mockReturnValueOnce(...) 或 .mockImplementation(...) 中动态处理;
- 使用 mockClear() 或 mockReset() 在每个测试后清理状态,防止副作用累积。
通过这种模式,你不仅能验证 Hook 是否被调用,还能灵活控制其返回值、模拟加载/错误状态,并与 React Testing Library 完美协同,构建高可信度的单元测试。

















