mock函数调用的核心目标是控制依赖行为、隔离被测代码、验证调用次数/参数/顺序;常用jest.fn()替换依赖,结合tobecalledwith、nthcalledwith等断言检查调用细节,并注意模块缓存与引用一致性。

在 JavaScript 测试中,Mock 函数调用的核心目标是:**控制依赖行为、隔离被测代码、验证是否按预期调用了函数(包括次数、参数、顺序等)**。常用方案是借助 Jest 的 jest.fn() 或手动替换函数引用,再结合断言检查调用记录。
用 jest.fn() 替换依赖函数
这是最直接的方式。把被测模块中依赖的函数(如 API 调用、工具方法)替换成 mock 函数,再在测试后检查其调用情况。
- 若函数来自外部模块(如
axios.get),用jest.mock('axios')+mockResolvedValue模拟返回,并通过axios.get.mock.calls查看调用参数 - 若函数是模块内导出的(如
utils.formatDate),可用jest.mock('./utils')并在 mock 实现中返回jest.fn() - 对于类方法或对象方法,可在测试前赋值:
const originalMethod = obj.method;
obj.method = jest.fn();
// 执行被测逻辑
expect(obj.method).toHaveBeenCalledWith('2023-01-01');
obj.method = originalMethod; // 恢复(可选,jest 通常自动清理)
验证调用细节的关键断言
Jest mock 函数提供多个内置断言方法,覆盖常见检查场景:
-
toBeCalled()或toHaveBeenCalled():确认至少调用过一次 -
toBeCalledTimes(n):精确匹配调用次数(例如防止重复请求) -
toBeCalledWith(arg1, arg2):检查最后一次调用的参数(支持部分匹配) -
nthCalledWith(n, arg1, arg2):检查第 n 次调用的参数(适合有顺序逻辑的场景) -
toHaveBeenNthCalledWith(1, 'id-123', { timeout: 5000 }):更语义化的写法,等价于上一条 -
mock.calls是二维数组(如[ ['a'], ['b', 1], [null] ]),可手动遍历做复杂校验
注意作用域与模块缓存问题
Mock 生效的前提是:**被测代码实际引用的是你 mock 后的函数**。常见陷阱包括:
- 未在
jest.mock()后重新require或import模块 → 模块缓存导致 mock 不生效 - 函数被内联引入(如
import { fn } from './utils'),需确保jest.mock('./utils')在 import 之前执行(Jest 会自动 hoistjest.mock,但需注意位置) - ESM 环境下,动态导入(
await import('./module'))需配合jest.unstable_mockModule(Jest 28+) - 避免在测试文件顶部多次
jest.mock同一模块,可能引发冲突;推荐统一在 describe 外部或 setup 文件中配置
不依赖 Jest 的轻量 Mock 方式
如果不用 Jest(如用 Vitest 或原生测试框架),可手动实现类似能力:
- 创建一个记录调用的函数:
const mockFn = jest.fn()的简易版:
const mockFn = { calls: [], mockImplementation: () => {}, ... };
const trackedFn = (...args) => { mockFn.calls.push(args); return mockFn.mockImplementation?.(...args); }; - 用
sinon.js:提供sinon.spy()、sinon.stub(),API 更丰富,兼容多种测试环境 - 对全局函数(如
fetch)可临时覆盖:global.fetch = jest.fn(),测试后用jest.restoreAllMocks()清理
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











