news 2026/9/25 17:30:34

sinon sandbox.verify:批量校验沙箱内全部 Mock 期望值的权威指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
sinon sandbox.verify:批量校验沙箱内全部 Mock 期望值的权威指南
  • 测试
  • 开发工具

【免费下载链接】sinon

Test spies, stubs and mocks for JavaScript.

项目地址:https://gitcode.com/gh_mirrors/si/sinon
点击查看免费下载

sandbox.verify()是 sinon 沙箱(sandbox)体系中用于一次性校验所有经由该沙箱创建的 Mock 期望是否被满足的入口方法。在默认沙箱模式下,它直接以sinon.verify()的形式调用,是编写基于 Mock 的单元测试时收尾断言的关键一步。读完本文,你将掌握verify的调用方式、失败时的异常行为与错误信息格式、它与其他沙箱方法(restore、verifyAndRestore)的分工,以及其底层实现原理(对应源码 src/sinon/sandbox.js 与 src/sinon/mock.js)。

沙箱与sandbox.verify的定位

在深入verify之前,需要先明确它所属的沙箱模型。自sinon@5.0.0起,sinon根对象本身就是一个默认沙箱(default sandbox),除非你有非常特殊的配置需求,否则通常只需要直接使用它,而不必调用sinon.createSandbox()创建新沙箱(参见 Sandbox API 总览 与 createSandbox 说明)。

沙箱的核心价值在于分组管理:通过同一个沙箱创建的所有 fakes(fake / spy / stub)与 mocks,可以统一执行restore、reset、verify等批量操作,避免逐一清理的繁琐与遗漏。

sandbox.verify负责的正是其中"校验"这一环,其官方语义是:

Verifies all mocks created through the sandbox. (校验所有经由该沙箱创建的 Mock。)

需要特别强调的是:sandbox.verify()只校验沙箱内创建的 Mock(及其期望 expectation),它不会去校验 spy/stub 的调用断言——那些断言通常通过sinon.assert或expect库完成。这一点从命名与实现上都与mock.verify(校验单个 Mock 的全部期望)形成对应关系。

基本用法:在默认沙箱上调用sinon.verify()

由于sinon根对象就是默认沙箱,sandbox.verify()在日常代码中通常写作sinon.verify()。下面的完整示例取自仓库中的对应测试 docs/tests/docs/sandboxes/api/verify.test.js,它演示了"创建 Mock → 设置期望 → 校验失败抛出异常"的完整链路:

import tap from "tap"; import * as sinon from "sinon"; tap.test("sandbox.verify - verifies all mocks", (t) => { // The sinon root object is a default sandbox const obj = { greet: function (name) { return `Hello ${name}`; } }; const mock = sinon.mock(obj); const expectation = mock.expects("greet"); // verify will throw because of the unmet expectation t.throws( () => sinon.verify(), /Expected greet\('\[...\]'\) once \(never called\)/, "throws when expectation not met" ); sinon.restore(); t.end(); });

逐段拆解这个流程:

  1. 创建 Mock:sinon.mock(obj)基于普通对象obj生成一个 Mock,并挂到默认沙箱的收集列表中;
  2. 设置期望:mock.expects("greet")声明对greet方法的一次调用期望(默认once,即期望恰好被调用一次);
  3. 触发校验:由于示例中从未调用obj.greet(...),期望未被满足,sinon.verify()会抛出异常;
  4. 验证异常:t.throws(...)断言抛出的异常信息匹配/Expected greet\('\[...\]'\) once \(never called\)/;
  5. 清理收尾:sinon.restore()恢复所有被替换/包装的方法,避免污染其他测试。

若期望被满足(例如在调用verify()之前执行了obj.greet("World")),则sinon.verify()静默返回、不抛异常,测试正常通过。

失败行为:异常抛出与错误信息格式

verify与普通断言的关键差异在于失败时的行为方式:当沙箱内任一 Mock 的任一期望未被满足时,sandbox.verify()会抛出异常(throw),而不是返回布尔值或仅打印警告。

异常消息会明确指出哪个方法、以何种参数、期望调用几次、实际调用情况。以上述示例为例,错误信息形如:

Expected greet('[...]') once (never called)

这条信息的结构对应 sinon Mock 期望(expectation)的字符串化输出:

  • greet:被期望的方法名;
  • '[...]':期望接收的参数模式([...]表示参数占位/匹配规则);
  • once:期望的调用次数(默认是恰好一次);
  • never called:实际调用情况(从未被调用)。

如果存在多个未满足的期望,错误信息会将它们逐条列出并聚合。从 src/sinon/mock.js 第 94–114 行的mock.verify实现可以看到:它对每个代理方法(proxy)逐一检查其全部期望,未满足的期望字符串被压入messages,已满足的进入met,随后:

if (messages.length > 0) { mockExpectation.fail(join(concat(messages, met), "\n")); } else if (met.length > 0) { mockExpectation.pass(join(concat(messages, met), "\n")); }

即只要有任一期望未满足,就通过mockExpectation.fail以换行分隔的聚合消息抛出失败;全部满足则走pass分支正常返回。

与mock.verify的层级关系

理解sandbox.verify的最佳方式,是与单点方法mock.verify对照:

方法作用范围失败行为附带效果
mock.verify()单个 Mock 对象上的全部期望抛出异常会恢复该 Mock 包装的所有方法(见 mock.verify 文档)
sandbox.verify()沙箱内收集的所有Mock抛出异常(首个/聚合失败)本身不执行恢复,需另行调用restore

mock.verify在 src/sinon/mock.js 中还有一个值得注意的细节:校验完成时会调用this.restore()恢复被 Mock 替换的方法。而sandbox.verify在 src/sinon/sandbox.js 第 289–291 行的实现则非常简洁:

sandbox.verify = function verify() { applyOnEach(collection, "verify"); };

它通过applyOnEach对沙箱收集列表collection中的每个条目调用其verify方法——所以沙箱级校验本质上就是"把mock.verify批量应用一遍"。沙箱自身不附带恢复逻辑,因此测试中通常要在verify之后(或通过verifyAndRestore)调用restore。

verifyAndRestore:校验与恢复一步到位

针对"校验失败也要保证清理"的常见场景,沙箱提供了组合方法sandbox.verifyAndRestore()(详见 verify-and-restore 文档),其语义为:

Verifies all mocks and restores all fakes created through the sandbox. (校验所有 Mock,并恢复所有经沙箱创建的 fakes。)

它的关键特性是:即使校验失败抛出了异常,恢复动作依然会执行。仓库测试 docs/tests/docs/sandboxes/api/verify-and-restore.test.js 完整验证了这一行为:

const obj = { greet: function (name) { return `Hello ${name}`; } }; const mock = sinon.mock(obj); const expectation = mock.expects("greet"); // mocked methods have a restore method on them t.equal(typeof obj.greet.restore, "function", "mocked method has restore function"); // verify will throw because of the unmet expectation t.throws( () => sinon.verifyAndRestore(), /Expected greet\('\[...\]'\) once \(never called\)/, "throws when expectation not met" ); // but the restore part will still be performed t.equal(typeof obj.greet.restore, "undefined", "method restored even though verify failed");

注意测试中的两个断言点:

  1. Mock 包装后的方法带上了restore函数(typeof obj.greet.restore === "function");
  2. 即使verifyAndRestore()因期望未满足而抛错,原方法也已被恢复(typeof obj.greet.restore === "undefined")。

其实现原理在 src/sinon/sandbox.js 第 293–307 行,通过try/catch捕获verify抛出的异常,无论成败都执行sandbox.restore(),最后再把异常重新抛出:

sandbox.verifyAndRestore = function verifyAndRestore() { let exception; try { sandbox.verify(); } catch (e) { exception = e; } sandbox.restore(); if (exception) { throw exception; } };

这样的设计保证了:单个用例即使 Mock 校验失败,也不会把被替换的方法残留到后续用例中,从而避免"失败级联污染"。

源码级原理:applyOnEach与 Mock 期望校验流程

如果把sandbox.verify的调用链展开,可以得到清晰的执行脉络:

  1. sinon.verify()→sandbox.verify()(src/sinon/sandbox.js);
  2. applyOnEach(collection, "verify"):遍历沙箱收集列表,对每个条目调用其verify方法;
  3. 对 Mock 条目,进入mock.verify(src/sinon/mock.js):
    • 遍历该 Mock 的每个代理方法(this.proxies);
    • 遍历每个方法的全部期望(expectations[proxy]),调用expectation.met()判断是否满足;
    • 未满足的期望字符串进入messages,满足的进入met;
    • 调用this.restore()恢复被替换的方法;
    • 若存在未满足期望,聚合所有消息后mockExpectation.fail(...)抛出异常;
  4. 异常向上传播,sandbox.verify()调用点直接收到抛出结果。

值得注意的边界行为:

  • verify只作用于沙箱内创建的 Mock。沙箱同样收集 spy/stub(它们也出现在collection中),但 spy/stub 并不具备verify语义,因此批量校验对它们无实际效果——这正是文档明确定义"校验所有 Mock"的原因;
  • sandbox.restore()不接受任何参数,如果误传参数会抛出"sandbox.restore() does not take any parameters. Perhaps you meant stub.restore()"的提示错误(见 src/sinon/sandbox.js),这也能帮助新手区分"沙箱级恢复"与"单个 stub 恢复"。

最佳实践与注意事项

结合文档与源码实现,使用sandbox.verify()时建议遵循以下实践:

  1. 默认优先用根沙箱:多数测试直接sinon.mock()+sinon.verify()即可,无需createSandbox()(create-sandbox.md 明确警告:除非有非常高级的场景,否则应使用sinon对象上的默认沙箱);
  2. 善用verifyAndRestore做收尾:在afterEach/t.teardown等清理钩子中调用sinon.verifyAndRestore(),可以同时完成"断言全部期望 + 恢复全部替换",且失败时也不遗漏恢复;
  3. 分离校验与恢复的关注点:如果希望先看到所有失败信息再做清理,或需要自定义恢复顺序,可分开调用sinon.verify()(置于断言区)与sinon.restore()(置于清理区);
  4. 理解错误信息的可读性:聚合的失败消息按方法逐个列出期望与调用情况,排错时优先看never called、called with wrong arguments等关键词定位未满足的期望;
  5. 不要指望verify校验 spy/stub 断言:spy/stub 的调用次数、参数等断言请使用sinon.assert或mock的期望机制,sandbox.verify只覆盖 Mock 期望。

通过默认沙箱 +sinon.verify()(或sinon.verifyAndRestore())的组合,你可以用最少的样板代码实现对 Mock 期望的集中校验与资源的可靠清理,这正是 sinon 沙箱设计中最实用的能力之一。

  • 测试
  • 开发工具

【免费下载链接】sinon

Test spies, stubs and mocks for JavaScript.

项目地址:https://gitcode.com/gh_mirrors/si/sinon
点击查看免费下载
上一篇:Renovate 的 Gradle Manager 深度解析:自定义解析器、Wrapper 安全执行与锁文件/依赖校验更新
下一篇:JDK jpackage Windows MSI 安装器 UI 控制事件(ControlEvents)深度解析:以 license+shortcut_prompt 组合为例

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/25 17:30:34

Atlas 300V 24G推理加速卡部署YOLO全流程:从环境搭建到性能调优

“atlas”这个词在AI圈里现在指向性已经很明确了——昇腾Atlas系列。最近后台不少人都在问两件事:一是“atlas部署yolo”到底怎么搞,二是“atlas 300v 24g 是运算加速卡吗”。这俩问题其实都指向同一个核心:这块24G大显存的卡能不能拿来跑目标…

作者头像 李华
网站建设 2026/9/25 17:27:24

wx_channels_download 的 Cloudflare 部署命令(deploy)实战指南:一键部署公众号 RSS、视频号查询与 Bridge 桥接 Worker

桌面应用视频网络MCP 服务 【免费下载链接】wx_channels_download 微信视频号下载器 项目地址: https://gitcode.com/gh_mirrors/wx/wx_channels_download 点击查看 免费下载 wx_channels_download 是一套集视频号、公众号内容抓取与下载于一体的工具。当你需要把公…

作者头像 李华
网站建设 2026/9/25 17:16:08

Windows下Neo4j社区版zip安装、配置与避坑指南

简介:面向图数据库学习者与开发运维人员,这是一份 Neo4j 5.23.0 社区版 Windows 安装压缩包,可离线部署并直接用于本地开发与教学。Neo4j 以节点和关系构成的图模型存储数据,可直观表达复杂关联,并通过 Cypher 声明式查…

作者头像 李华