Sinon 中 mock.restore 完全指南:还原所有被 Mock 的方法及其底层实现原理
测试开发工具【免费下载链接】sinonTest spies, stubs and mocks for JavaScript.项目地址https://gitcode.com/gh_mirrors/si/sinon点击查看免费下载mock.restore()是 Sinon 中Mock对象提供的关键方法用于将对象上所有被 Mock 的方法还原为原始实现。本文以docs/concepts/mocks/api/restore.md为核心结合仓库中的测试用例docs/tests/docs/mocks/api/restore.test.js与源码实现src/sinon/mock.js、src/sinon/util/core/wrap-method.js展开帮助你掌握mock.restore()的调用方式、验证机制、与verify()/沙箱的协作关系以及它在测试生命周期中扮演的角色。一、mock.restore是什么在 Sinon 的 Mock API 中mock.restore()的作用非常明确——还原Restore所有被 Mock 的方法。当你通过sinon.mock(obj)创建一个 mock 对象并通过mock.expects(methodName)为对象的方法设置预编程期望expectation后Sinon 会用一个代理函数**替换wrap**原对象上的真实方法。mock.restore()就是用来撤销这一替换动作的它遍历 mock 对象记录的所有代理方法调用每个代理方法自带的restore函数把对象属性还原回最初的原始实现。mock.restore();这是 Mock API 三大核心方法之一。根据docs/concepts/mocks/api/_index.mdmock 对象主要提供expects——为指定方法创建期望restore——还原所有被 Mock 的方法verify——校验所有期望是否被满足并自动还原。二、基本用法与完整示例1. 来自官方文档的核心描述官方文档docs/concepts/mocks/api/restore.md对它的描述只有一句话Restores all mocked methods.还原所有被 Mock 的方法。但这背后蕴含的测试生命周期语义需要结合仓库中的测试用例来完整理解。2. 官方测试用例逐行解读仓库中为mock.restore提供了专门的测试文件docs/tests/docs/mocks/api/restore.test.js完整展示了它的使用场景import tap from tap; import * as sinon from sinon; tap.test(mock.restore - restores all mocked methods, (t) { const obj { greet: function (name) { return Hello ${name}; } }; const mock sinon.mock(obj); const expectation mock.expects(greet); obj.greet(Mickey Mouse); // mocked methods have a restore method on them t.equal( typeof obj.greet.restore, function, mocked method has restore function ); mock.restore(); // the original greet method has been restored t.equal( typeof obj.greet.restore, undefined, restored method no longer has restore function ); t.end(); });这段测试揭示了三个关键事实Mock 过程会替换原方法mock.expects(greet)之后obj.greet不再是原始函数而是一个被包装wrapped的代理方法并且该代理方法上额外挂载了一个restore函数属性typeof obj.greet.restore function。mock.restore()还原全部方法调用mock.restore()后obj.greet被还原为原始实现其上的restore属性也随之消失typeof obj.greet.restore undefined说明对象属性已恢复到 Mock 之前的真实状态。调用方式无需传参mock.restore()不需要任何参数会自动处理该 mock 对象上所有被expects过的代理方法。3. 一个可运行的完整示例结合上面测试的写法在真实测试套件中这里以 tap 为例也适用于 Mocha、Jest 等任意测试框架可以这样使用import * as sinon from sinon; test(myService 在被 mock 后可以完整还原, () { const service { fetch: async () real-data, save: () saved }; const mock sinon.mock(service); mock.expects(fetch).resolves(mock-data); mock.expects(save).once(); // ... 执行业务逻辑触发被 mock 的方法 ... mock.verify(); // 校验期望是否被满足同时也会还原方法 // 或者手动mock.restore(); // 只还原方法不做校验 });三、底层实现mock.restore在源码中做了什么1. 核心实现位于src/sinon/mock.jsmock.restore的实现非常简洁位于 src/sinon/mock.jsrestore: function restore() { const object this.object; each(this.proxies, function (proxy) { if (typeof object[proxy].restore function) { object[proxy].restore(); } }); },逐行分析其工作流程this.object即sinon.mock(obj)传入的原始对象创建时通过extend.nonEnum({}, mock, { object: object })挂载在 mock 对象上见 mock.create。this.proxies记录所有被expects过的方法名数组。在 mock.expects 中第一次为某个方法创建期望时会执行wrapMethod(this.object, method, ...)包装方法并把方法名push(this.proxies, method)记录下来。逐代理还原遍历proxies对每个方法名先判断object[proxy].restore是否为函数——也就是前文测试中断言的“mocked method has restore function”——然后调用它从而把方法还原为原始实现。2.restore属性从哪来wrap-method.js被 mock 的方法上为什么会自带一个restore函数答案在src/sinon/util/core/wrap-method.js。该方法负责将对象属性替换为代理并在代理上通过extend.nonEnum挂载了以下元数据见 wrap-method.jsextend.nonEnum(target, { displayName: property, wrappedMethod: wrappedMethods[i], // 保存原始方法的引用 stackTraceError: new Error(Stack Trace for original), restore: restore, // 每个被包装的方法都自带 restore }); target.restore.sinon true;关键点wrappedMethod被包装前原始方法的引用被保存下来这正是restore能够“回到过去”的凭据。restore函数的还原策略针对不同的对象属性形态自有属性 vs 原型链属性、访问器 get/set vs 普通数据属性、ES5 支持与否wrap-method.js内部实现了对应的还原逻辑——能delete就delete原型属性场景否则用Object.defineProperty写回原始 descriptor或直接赋值回wrappedMethod见 wrap-method.js。因此mock.restore()并非简单赋值而是“尽力恢复原始属性描述符”的精细化操作。重复包装防护restore.sinon true标记用于检测“Attempted to wrap ... which is already wrapped”避免对同一方法进行二次包装导致还原链混乱。3. 匿名 Mock 的特殊情形mock(object)的源码mock.js支持两种入参export default function mock(object) { if (!object || typeof object string) { return mockExpectation.create(object ? object : Anonymous mock); } return mock.create(object); }传入对象走mock.create(object)返回一个带expects/restore/verify方法的 mock 对象restore按上述逻辑还原对象方法不传对象或传入字符串名称返回一个匿名 mockAnonymous mock本质上是独立的期望函数没有对象方法可还原也就无需也无法调用mock.restore()。四、mock.restore与mock.verify的关系理解mock.restore的一个重要维度是它与mock.verify的协作。从 mock.js 中的 verify 实现 可以看到verify: function verify() { const expectations this.expectations || {}; const messages this.failures ? slice(this.failures) : []; const met []; each(this.proxies, function (proxy) { each(expectations[proxy], function (expectation) { if (!expectation.met()) { push(messages, String(expectation)); } else { push(met, String(expectation)); } }); }); this.restore(); // 关键verify 内部也会调用 restore if (messages.length 0) { mockExpectation.fail(join(concat(messages, met), \n)); } else if (met.length 0) { mockExpectation.pass(join(concat(messages, met), \n)); } return true; },由此可以得出一个对测试编写至关重要的结论mock.verify()在校验完所有期望后会自动调用this.restore()还原所有被 mock 的方法然后才根据期望满足情况pass/fail。这一点在测试文件docs/tests/docs/mocks/api/verify-1.test.js中得到了印证——该测试在mock.verify()之后断言typeof obj.greet.restore undefined方法已被还原。因此如果你只关心期望校验调用mock.verify()即可它会顺手完成还原如果你想在不校验或稍后校验的情况下先释放对象可以单独调用mock.restore()重复调用是安全的restore遍历proxies时会先判断typeof object[proxy].restore function已被还原的方法不再满足该条件会被静默跳过不会抛错。五、与沙箱sandbox的配合在大型测试套件中更常见的做法是使用 Sinon 沙箱统一管理生命周期。沙箱的restore文档docs/concepts/sandboxes/api/restore.md指出sandbox.restore()还原所有通过沙箱创建的fakes、spies和stubs。沙箱体系与mock.restore是互补的两条路径手动管理sinon.mock(obj)创建独立 mock测试结束时手动调用mock.restore()或mock.verify()触发隐式还原沙箱管理使用sinon.createSandbox()统一创建 spy/stub/mock由sandbox.restore()在afterEach等钩子中统一还原避免遗忘导致的测试间状态泄漏。具体沙箱用法可参考 docs/concepts/sandboxes/create-sandbox.md 与 docs/concepts/sandboxes/api/restore.md。六、最佳实践与注意事项每个测试至多一个 mockdocs/concepts/mocks/index.md明确建议mock 只应作用于“被测方法”本身一般单个测试中不要超过一个 mock可含多个 expectation过度使用会强制测试实现细节。若你只想替换行为而不做前置期望应改用 stubs或参考 fakes 配合显式断言。及时还原防止泄漏被 mock 的方法不还原会污染后续测试尤其是afterEach中未清理时。优先在afterEach中调用mock.restore()或直接依赖mock.verify()的隐式还原。验证“还原”本身如官方测试所示可以通过typeof obj.method.restore undefined断言方法确已还原也可以直接调用原方法检查返回值是否恢复真实行为。不要对同一方法重复 mockwrap-method.js的checkWrappedMethod会检测“already wrapped/stubbed/spied on”并抛出TypeError若先mock.restore()再重新expects则是安全的。匿名 mock 没有 restore 语义sinon.mock(name)返回的是独立期望函数不属于“还原对象方法”的范畴不需要也无法调用mock.restore()。七、结语mock.restore()虽然 API 表面只有一句话——“Restores all mocked methods”——但它在测试生命周期中承担着“安全着陆”的关键职责从mock.expects的包装替换到wrap-method.js中对原始方法引用wrappedMethod的保存与精细化还原再到mock.verify()内部隐式调用restore整条链路共同保证了 Mock 不会把测试状态泄漏到测试之外。掌握它你就能安全、放心地使用 Sinon 的 Mock 能力编写前置期望式测试。相关资源索引官方文档docs/concepts/mocks/api/restore.md、docs/concepts/mocks/api/_index.md官方测试docs/tests/docs/mocks/api/restore.test.js、docs/tests/docs/mocks/api/verify-1.test.js核心源码src/sinon/mock.js、src/sinon/util/core/wrap-method.js关联概念Mocks 总览、Expectations、沙箱 restore赞分享测试开发工具【免费下载链接】sinonTest spies, stubs and mocks for JavaScript.项目地址https://gitcode.com/gh_mirrors/si/sinon点击查看免费下载相关推荐Jest ES6 Class Mocks 完全指南4 种 Mock 方式与底层原理剖析Jest ES6 Class Mocks 完全指南4 种 Mock 方式与底层原理剖析 ES6 类本质上只是带语法糖的构造函数因此对 ES6 类的 Mock测试质量保障代码覆盖率开发工具Sinon Sandbox 中的 mock 方法在沙箱内创建与管理 Mock 的完整指南Sinon Sandbox 中的 mock 方法在沙箱内创建与管理 Mock 的完整指南 导读 本文以 docs/concepts/sandboxes/api测试开发工具Cytoscape.js 元素动画完全指南eles.animate() 的 API 用法与底层实现原理Cytoscape.js 元素动画完全指南eles.animate 的 API 用法与底层实现原理 本文聚焦 Cytoscape.js 图库中 元素集合co数据可视化创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

相关新闻

win10下yolov6 tensorrt模型部署

win10下yolov6 tensorrt模型部署

TensorRT系列之 Win10下yolov8 tensorrt模型加速部署 TensorRT系列之 Linux下 yolov8 tensorrt模型加速部署 TensorRT系列之 Linux下 yolov7 tensorrt模型加速部署 TensorRT系列之 Linux下 yolov6 tensorrt模型加速部署 TensorRT系列之 Linux下 yolov5 tensorrt模型加速部署…

2026/9/25 6:14:53 阅读更多 →
win10下yolov7 tensorrt模型部署

win10下yolov7 tensorrt模型部署

TensorRT系列之 Win10下yolov8 tensorrt模型加速部署 TensorRT系列之 Linux下 yolov8 tensorrt模型加速部署 TensorRT系列之 Linux下 yolov7 tensorrt模型加速部署 TensorRT系列之 Linux下 yolov6 tensorrt模型加速部署 TensorRT系列之 Linux下 yolov5 tensorrt模型加速部署…

2026/9/25 6:14:53 阅读更多 →
源师兄手柄按键技巧:如何精准捕捉 C/D/E/F 按键的按下与松开?

源师兄手柄按键技巧:如何精准捕捉 C/D/E/F 按键的按下与松开?

源师兄手柄按键技巧:如何精准捕捉 C/D/E/F 按键的按下与松开? 【免费下载链接】gamepad 源师兄扩展项目: 手柄 | 由源师兄组织创建 项目地址: https://gitcode.com/yuanshixiong/gamepad 源师兄手柄(gamepad) 是由源师兄组…

2026/9/25 6:14:53 阅读更多 →

最新新闻

Codex全破甲v1.4.0:大模型指令强化在渗透与逆向中的工程化落地

Codex全破甲v1.4.0:大模型指令强化在渗透与逆向中的工程化落地

1. “全破甲”不是营销话术,而是指令工程在安全领域的硬核落地Codex 全破甲 v1.4.0 这个名字里,“全破甲”三个字乍看像玄幻小说里的设定,但放在渗透测试和逆向分析这个语境下,它指向一个非常具体、可验证的技术事实:该…

2026/9/25 6:48:18 阅读更多 →
Dart SDK 内置 vm_snapshot_analysis 变更日志全解:0.1.0 至 0.7.6 版本演进与源码印证

Dart SDK 内置 vm_snapshot_analysis 变更日志全解:0.1.0 至 0.7.6 版本演进与源码印证

编程语言编译器语言运行时标准库开发工具 【免费下载链接】sdk The Dart SDK, including the VM, JS and Wasm compilers, analysis, core libraries, and more. 项目地址: https://gitcode.com/gh_mirrors/sdk1/sdk 点击查看 免费下载 本文基于 Dart SDK 仓库中的…

2026/9/25 6:48:18 阅读更多 →
AMD平台RL训练bitwise一致:RL-Kernel与vime实战解析

AMD平台RL训练bitwise一致:RL-Kernel与vime实战解析

如果你问我,在 RL 训练系统里最容易藏雷的地方是哪里,我不会先说分布式采样、内存泄漏或者梯度过大,而是“训练和 rollout 在数值上差了最后几个 bit”。尤其是当训练侧跑在 RL-Kernel 这类自研内核上,环境侧跑在 vime 这类模拟执…

2026/9/25 6:48:18 阅读更多 →
树莓派4B装ROS2 Humble完整指南:从系统配置到环境部署

树莓派4B装ROS2 Humble完整指南:从系统配置到环境部署

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/25 6:48:17 阅读更多 →
Atlas 300V 24G推理卡YOLO部署全流程实战指南

Atlas 300V 24G推理卡YOLO部署全流程实战指南

上个月帮客户做一套边缘巡检方案,前前后后折腾了两周,最终选定的硬件方案里就有华为的 Atlas 300V 24G 推理加速卡。坦率讲,这张卡在圈子里的热度一直不低,但真把它用来跑 YOLO 系列模型,从环境搭建到模型转换&#xf…

2026/9/25 6:48:17 阅读更多 →
运营人必备的四大核心思维解析

运营人必备的四大核心思维解析

1. 运营人必备的四大核心思维解析在互联网行业摸爬滚打这些年,我见过太多运营新人把精力都花在学习各种工具和技巧上,却忽视了最基础的思维建设。就像盖房子不打地基,表面功夫做得再漂亮也经不起市场考验。今天我要分享的这四个思维模型&…

2026/9/25 6:47:17 阅读更多 →

日新闻

AI元人文:从工具使用到思维重构的深度探索

AI元人文:从工具使用到思维重构的深度探索

最近半年我一直在琢磨一件事:AI元人文到底是什么?说白了,就是“用元视角重新审视人与AI的关系”,也在“探索AI如何反向逼着我们发现自己的思考边界”。标题里的“元探索”,在我看就是一层套一层的追问——当你用AI解决…

2026/9/25 0:00:41 阅读更多 →
Python+CNN车牌识别实战:从数据预处理到模型训练与部署

Python+CNN车牌识别实战:从数据预处理到模型训练与部署

简介:基于Python与卷积神经网络的车牌识别项目,面向计算机视觉初学者及智能交通开发者,目标是帮助用户掌握从数据预处理、模型构建到实际部署的完整流程。压缩包共25个文件,包含jpg/png图像样本、py训练脚本、md说明文档、dat数据…

2026/9/25 0:00:41 阅读更多 →
Vim基础操作全攻略:保存退出、模式切换与高频命令实战

Vim基础操作全攻略:保存退出、模式切换与高频命令实战

1. 项目概述1.1 核心需求解析今天聊聊Vim。写这个题目的原因是:几乎每个后端开发者、运维人员、数据工程师某天都会遇到一个场景——深夜加班,服务器登录界面只有黑底白字,编辑器只有vi/vim,你必须在五分钟内完成一次配置修改并保…

2026/9/25 0:00:41 阅读更多 →

周新闻

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

直接铺开项目本身吧。这几个月我一直在折腾一件事:用Flutter给OpenHarmony做一款游戏集合类的App,说白了就是把若干小游戏塞进一个壳里,用统一入口分发。这个方向本身不算新鲜,真正让我花了不少心思的,是首页那堆游戏卡…

2026/9/24 14:34:13 阅读更多 →
Word表格编号全攻略:从列表编号到题注交叉引用

Word表格编号全攻略:从列表编号到题注交叉引用

写Word文档,最让人头疼的往往是那些“看起来不起眼”的小问题。比如表格编号这事:今天在表后面多加了两个空白行,明天给客户交稿前发现整个章节的编号全部错位,光是挨个改序号就能耗掉大半个下午。我前阵子帮人整理一份上百页的技…

2026/9/24 9:10:42 阅读更多 →
从第一个站到第二个站:独立开发者的静态网站选型与落地实践

从第一个站到第二个站:独立开发者的静态网站选型与落地实践

1. 项目概述1.1 核心需求解析做独立开发者这几年,说实话,第一个网站上线的那天晚上我兴奋得没睡着。但等它跑了半年,流量惨淡、功能臃肿、代码自己都懒得看第二遍之后,我才慢慢琢磨明白一个道理:第一个网站是练手&…

2026/9/24 14:33:56 阅读更多 →

月新闻

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能分类:[AI/大模型]细分主题:AI 增强型 CI/CD 流水线自动化与 GitOps 实践:Agent 工作流、工具调用与任务拆解:从原型到生产的验收清单很多团队在尝试用大…

2026/9/24 12:50:34 阅读更多 →
容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场分类:[工程技术]细分主题:Kubernetes 生产环境运维与排障实战:可复制的项目复盘模板与决策记录大部分团队的事故复盘报告,最后都变成了躺在 Confluence 或钉…

2026/9/24 14:33:48 阅读更多 →
容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步分类:[工程技术]细分主题:Docker 容器化技术与镜像安全管理:核心链路的逐步实现与关键代码取舍面对一个积累了五六年历史包袱的单体架构应用(包含 Web 接口、后台…

2026/9/24 12:49:17 阅读更多 →