jest 模拟第三方工具函数的核心是用 jest.fn() 替换目标函数并控制其行为,关键在替换时机与作用域可见性;需按导入方式(模块/全局/动态)选择 jest.mock()、全局赋值或 jest.domock(),并用 mock 属性断言调用及还原状态。

在 JS 单元测试中,用 Jest 模拟第三方工具函数,核心是用 jest.fn() 替换目标函数,并控制其行为,而不是直接修改原始模块。关键在于“替换时机”和“作用域可见性”——必须确保被测代码调用的是 mock 版本,且测试代码能访问该 mock 实例进行断言。
直接模拟导入的工具函数
当工具函数以具名或默认方式从第三方包(如 lodash、date-fns)导入时,推荐在测试文件顶部用 jest.mock() 拦截整个模块,再通过 jest.requireActual() 有选择地保留部分真实逻辑:
- 在
import语句前调用jest.mock('lodash') - 用
jest.requireActual('lodash')获取原模块,只 mock 需要控制的函数(比如debounce),其余保持真实行为 - 示例:
jest.mock('lodash', () => {<br> const original = jest.requireActual('lodash');<br> return {<br> ...original,<br> debounce: jest.fn((fn) => fn)<br> };<br>});
模拟全局或挂载在对象上的工具函数
像 window.fetch、console.log 或第三方 SDK 挂载在 globalThis 上的函数,可直接用 jest.fn() 覆盖:
- 测试前保存原始引用(可选):
const originalFetch = global.fetch; - 赋值 mock:
global.fetch = jest.fn(); - 设置返回值:
global.fetch.mockResolvedValue({ json: () => ({ ok: true }) }); - 测试后还原(避免污染其他用例):
global.fetch = originalFetch;或使用afterEach(() => { global.fetch.mockReset(); });
处理 ESM 动态导入或按需加载的工具
对通过 import(...) 加载的工具(如某压缩库的解压函数),不能用顶层 jest.mock(),需在测试中内联 mock:
- 用
jest.mock('./utils/compressor.js')模拟该路径模块(即使它本身是第三方封装) - 或在测试块内用
jest.doMock()+require()实现局部生效:jest.doMock('./utils/compressor', () => ({ decompress: jest.fn().mockReturnValue('mocked data') }));<br>const { processFile } = require('./fileHandler'); - 注意:每次
doMock后建议配合jest.resetModules()清除缓存,防止模块复用影响结果
验证调用与还原状态
模拟不是目的,精准验证才是。所有 mock 函数都自带 mock 属性,可用于断言:
- 检查是否被调用:
expect(debounce).toHaveBeenCalledTimes(1) - 核对参数:
expect(fetch).toHaveBeenCalledWith('/api/user', expect.any(Object)) - 确认返回值链路:
await expect(apiCall()).resolves.toEqual({ id: 1 }) - 每个测试结束后调用
jest.clearAllMocks()或jest.restoreAllMocks(),避免 mock 状态跨用例残留











