CodeWalk

Jest mock 函数原理与模拟模块(jest.mock/jest.spyOn)

作者:屠龙少年 · 2026-05-30 12:55

请解释 Jest 中 jest.fn()、jest.mock()、jest.spyOn() 三种模拟方式的区别与使用场景,以及 mock 函数的内部实现原理。

回答

屠龙少年

三种模拟方式

1. jest.fn(implementation):创建模拟函数

const mockFn = jest.fn((a, b) => a + b);
mockFn(1, 2);
console.log(mockFn.mock.calls[0]); // [[1, 2]]
console.log(mockFn.mock.results[0].value); // 3
  • 记录每次调用的参数、返回值、this 上下文
  • 链式方法:mockImplementationmockReturnValuemockResolvedValue

2. jest.mock(modulePath, factory):自动模拟整个模块

jest.mock('axios');
// 所有 axios 方法被替换为 jest.fn()
const axios = require('axios');
axios.get.mockResolvedValue({ data: 'mock' });
  • 模块级模拟,在 import 前调用(hoisted)
  • 路径基于 mocks 目录自动查找

3. jest.spyOn(object, methodName):监视真实对象方法

const spy = jest.spyOn(console, 'log');
console.log('test');
expect(spy).toHaveBeenCalledWith('test');
spy.mockRestore(); // 恢复原始实现
  • 默认调用真实实现,可通过 .mockImplementation() 覆盖
  • 适合部分模拟,保留大部分行为

内部原理

  • jest.fn() 生成一个包装函数,内部维护 _mockState 存储 calls/instances/results
  • jest.mock() 在模块缓存中替换模块工厂函数
  • jest.spyOn()Object.defineProperty 替换对象方法为 mock

最佳实践:优先 spyOn(保留真实性),复杂场景用 mock,简单函数用 fn。