Jest mock 函数原理与模拟模块(jest.mock/jest.spyOn)
请解释 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 上下文
- 链式方法:
mockImplementation、mockReturnValue、mockResolvedValue
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。