JavaScript 中 spy 是监听函数调用而不改变行为的测试工具,支持记录调用次数、参数、返回值等;Jest 用 jest.spyOn() 监视对象已有方法,Sinon 用 sinon.spy() 可监视任意函数;需对调用链中各关键函数分别设置 spy,并注意模块导出、箭头函数及清理操作。

在 JavaScript 中,spy 通常指对函数进行“监听”而不改变其行为的测试辅助手段,常见于测试框架(如 Jest、Sinon)中。它不修改原函数逻辑,但能记录调用次数、参数、返回值、执行顺序等,从而帮助你观察方法内部调用流程。
使用 Jest 的 jest.spyOn() 监视对象方法
Jest 内置了轻量级 spy 功能,适合监视已存在对象上的方法:
- 必须作用于对象的**已有属性(方法)**,不能直接 spy 普通函数变量(除非先挂到对象上)
- spy 默认保留原方法行为(即“真实调用”),同时记录调用信息
- 调用后可通过
.mock.calls、.mock.results等属性查看执行细节
示例:
const calculator = {
add: (a, b) => a + b,
multiply: (a, b) => a * b
};
<p>// 创建 spy,监视 multiply 方法
const multiplySpy = jest.spyOn(calculator, 'multiply');</p><p>calculator.add(2, 3); // 不被 spy
calculator.multiply(4, 5); // 被记录:一次调用,参数 [4, 5],返回 20</p><p>console.log(multiplySpy.mock.calls); // [[4, 5]]
console.log(multiplySpy.mock.results); // [{ type: 'return', value: 20 }]用 Sinon 实现更灵活的 spy(含独立函数监视)
Sinon 提供更底层的 sinon.spy(),可 spy 任意函数(包括匿名函数、模块导出函数),也支持包装对象方法:
立即学习“Java免费学习笔记(深入)”;
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
-
sinon.spy(obj, 'methodName'):类似 Jest,代理对象方法 -
sinon.spy(fn):返回一个带监控能力的新函数,需手动替换原调用位置 - 所有调用记录统一存于
spy.callCount、spy.getCall(n)、spy.args等属性中
示例(监视独立函数):
function fetchData(url) {
return fetch(url).then(r => r.json());
}
<p>const fetchSpy = sinon.spy(fetchData);
fetchSpy('<a href="https://www.php.cn/link/46b315dd44d174daf5617e22b3ac94ca">https://www.php.cn/link/46b315dd44d174daf5617e22b3ac94ca</a>'); // 调用被记录</p><p>console.log(fetchSpy.callCount); // 1
console.log(fetchSpy.getCall(0).args); // ['<a href="https://www.php.cn/link/46b315dd44d174daf5617e22b3ac94ca">https://www.php.cn/link/46b315dd44d174daf5617e22b3ac94ca</a>']监视方法内部调用链(嵌套调用流程)
单纯 spy 外层方法无法自动捕获其内部调用的其他函数。若要追踪完整流程,需对**所有关键路径上的函数**都设置 spy,并注意调用上下文:
- 确保被 spy 的函数是实际被执行的那个引用(避免箭头函数或重新赋值导致丢失)
- 若方法内调用了另一个对象的方法(如
this.logger.log()),也要对logger.log单独 spy - 可结合
console.log或断点调试辅助验证 spy 是否生效
示例(监视调用链):
const service = {
process: function(data) {
this.validate(data);
return this.transform(data);
},
validate: (d) => d && typeof d === 'object',
transform: (d) => ({ ...d, processed: true })
};
<p>// 分别 spy 两个内部方法
const validateSpy = jest.spyOn(service, 'validate');
const transformSpy = jest.spyOn(service, 'transform');</p><p>service.process({ id: 1 }); // 触发 validate → transform</p><p>console.log(validateSpy.mock.calls.length); // 1
console.log(transformSpy.mock.calls.length); // 1注意事项与常见陷阱
- spy 是浅层代理:只监控函数被调用的事实,不拦截或重写执行逻辑(除非配合
mockImplementation) - ES6 模块默认导出函数时,Jest 中需用
jest.mock('./module')+requireActual才能正确 spy,否则可能得到 undefined - 测试结束后记得调用
mockRestore()(Jest)或spy.restore()(Sinon),避免影响其他测试 - 不要 spy 箭头函数定义在局部作用域且未挂载到对象上的情况——它没有“所属对象”,无法被
spyOn定位

















