Sinon 断言指南:`assert.called`——验证 spy、stub 与 fake 至少被调用过一次

发布时间:2026/9/24 16:26:33

Sinon 断言指南:`assert.called`——验证 spy、stub 与 fake 至少被调用过一次 测试开发工具【免费下载链接】sinonTest spies, stubs and mocks for JavaScript.项目地址https://gitcode.com/gh_mirrors/si/sinon点击查看免费下载assert.called(spy)是 Sinon 内置断言 API 中最基础的断言之一只要传入的fake、spy或stub在测试期间至少被调用过一次断言即通过否则抛出一个带有详细描述信息的AssertError。本文基于当前仓库中 called.md 文档并结合 assert.js 源码与对应的 单元测试从使用方式、错误消息、底层实现到相关断言家族完整讲解这一断言在测试中的定位与实战用法。断言签名与语义assert.called(spy);语义非常直接校验目标函数是否被调用过至少一次。只要调用次数 ≥ 1断言通过调用次数为 0断言失败。它适用于 Sinon 三类测试替身test doublespy包装原始函数或独立存在记录调用信息但不改变行为stub可预设行为的替身同样记录调用信息fake更现代、更轻量的独立替身与 spy 拥有相同的调用记录能力。因此凡是能记录调用历史的 Sinon 代理对象都可以作为assert.called的参数。快速上手在 ESM 环境下从sinon包导入 API 后即可使用参见 called.md 中的基础示例import * as sinon from sinon; const spy sinon.spy(); // 此时 spy 尚未被调用断言失败 sinon.assert.called(spy); // Error [AssertError]: expected spy to have been called at least once but was never called spy(); // 调用一次 // 现在断言通过不产生任何异常 sinon.assert.called(spy);关键行为有两点失败即抛错断言失败时抛出的异常name为AssertError见 assert.js 中fail的实现可以直接被测试框架捕获并判定用例失败成功静默断言通过时不返回任何值、不产生异常配合assert.pass的内部机制静默返回见 assert.js。在测试框架中使用仓库为每个文档 API 都配套了可运行的测试用例。assert.called对应的测试位于 docs/tests/docs/assertions/api/called.test.js使用tap编写覆盖了调用后通过与未调用即失败两个方向import tap from tap; import * as sinon from sinon; tap.test(assert.called - passes when spy was called, (t) { const spy sinon.spy(); spy(); t.doesNotThrow(() { sinon.assert.called(spy); }, assertion should pass); t.end(); }); tap.test(assert.called - fails when spy was not called, (t) { const spy sinon.spy(); t.throws( () sinon.assert.called(spy), /expected spy to have been called at least once but was never called/, assertion should fail with descriptive message ); t.end(); });这套测试同时也验证了两件事失败消息的文本是稳定可匹配的契约正则中直接匹配了错误文案以及断言通过时确实不会抛出异常。在实际的 Jest / Mocha / Vitest 项目中可直接把断言放进it/test回调中失败时框架会自动收集AssertError。失败时的错误消息是如何生成的assert.called的默认失败消息模板定义在 assert.jsmirrorPropAsAssertion( called, expected %n to have been called at least once but was never called, );其中%n是格式化占位符由 spy-formatters.js 中的n处理器替换为spyInstance.toString()即 spy 的名称描述如spy。消息组装完成后通过(fake.printf || fake.proxy.printf).apply(...)渲染出来见 assert.js最终抛出。与它同族的断言还有calledOnce、calledTwice、calledThrice它们会额外用%c占位符输出实际的调用次数英文描述如once、twice、thrice由timesInWords生成例如expected spy to be called once but was called twicecalledOnce 失败时也就是说assert.called的失败消息聚焦于从未被调用这一种失败形态而次数敏感型断言如calledOnce则能进一步告诉你实际被调用了几次。源码视角assert.called的底层实现assert.called并不是手写的一个独立函数而是通过mirrorPropAsAssertion这个工厂函数批量生成的见 assert.js。其执行流程如下校验参数verifyIsStub(fake)assert.js先确认传入的是有效的 Sinon 代理对象——若传入null会报fake is not a spy若对象没有getCall方法会报fake is not stubbed校验参数个数verifyIsValidAssertionassert.js规定called不接受额外参数多传参数会直接报错called takes 1 argument but was called with N arguments读取布尔属性对called而言meth未提供函数因此直接读取fake.called这个布尔属性assert.js判定与输出failed为真时调用failAssertion渲染并抛出AssertError否则调用assert.pass静默通过。fake.called属性由谁维护assert.called读到的called布尔属性是 Sinon 代理对象在每次被调用时由incrementCallCount同步更新的见 proxy-call-util.jsexport function incrementCallCount(proxy) { proxy.called true; proxy.callCount 1; proxy.notCalled false; proxy.calledOnce proxy.callCount 1; proxy.calledTwice proxy.callCount 2; proxy.calledThrice proxy.callCount 3; }代理在初始化时called被置为false见 proxy.js此后每调用一次就置true并递增callCount。因此assert.called本质上是对spy.called true这一状态的断言封装——你也可以在代码里直接读取spy.called做条件判断但用assert.called能获得统一、描述清晰的失败消息。与相关断言的组合使用assert.called只回答有没有被调用过这一个问题。当测试需要更强的约束时可以按需组合 Assertions API 家族中的其他成员完整列表见 docs/concepts/assertions/api/场景推荐断言语义至少调用一次assert.called本次讨论的断言一次都没调用assert.notCalled与called互斥失败消息为expected spy to not have been called but was called %c%C恰好调用一次assert.calledOnce失败时会输出实际次数恰好两次 / 三次assert.calledTwice/assert.calledThrice次数精确断言精确参数assert.calledWith/assert.calledWithExactly结合参数校验以 matcher 匹配参数assert.calledWithMatch结合 matchers 使用精确调用次数assert.callCount断言具体次数数值一个常见组合是先确认被调用再确认调用参数const spy sinon.spy(); doWork(spy); sinon.assert.called(spy); // 1. 至少调用了一次 sinon.assert.calledWith(spy, key, 42); // 2. 且最后一次调用参数正确注意assert.called本身不校验参数、不校验this上下文、不校验调用次数上限这些分别由calledWith、calledOn、calledOnce等断言承担按需组合即可。进阶实践与注意事项1. 包装真实方法后再断言除了独立的sinon.spy()最常见的是对真实对象方法做包装验证某个依赖是否被触发const service { save(data) { /* 真实逻辑 */ } }; const saveSpy sinon.spy(service, save); service.save({ id: 1 }); sinon.assert.called(saveSpy); saveSpy.restore(); // 记得恢复原始方法2. 与 stub 一起使用stub 同样记录调用信息因此assert.called也可用于验证某个被替换的依赖是否被调起const stub sinon.stub().returns(42); compute(stub); sinon.assert.called(stub);3. 配合assert.expose批量暴露到测试对象如果希望断言以更贴近阅读习惯的形式出现可以使用assert.expose把整个断言对象批量挂载到目标对象上见 assert.js 的expose实现支持prefix与includeFail选项。例如挂到全局后即可写assert.called(spy)而无需每次sinon.assert.前缀。4. 在沙箱sandbox中使用配合sinon.createSandbox()可以把替身与断言放在同一作用域内统一恢复const sandbox sinon.createSandbox(); const spy sandbox.spy(); run(); sandbox.assert.called(spy); sandbox.restore();5. 注意事项只验证至少一次若测试要求恰好一次请改用calledOnce否则调用两次时assert.called依然静默通过可能掩盖回归不接受额外参数assert.called(spy, somethingElse)会直接抛错而非忽略多余参数参数必须是 Sinon 代理普通函数或非 spy 对象会触发fake is not a spy/fake is not stubbed等前置校验错误错误类型为AssertError断言失败抛出的是名为AssertError的普通Error可被任何主流测试框架识别若开启了shouldLimitAssertionLogs选项长日志会被截断到assertionLogLimit默认 10K见 assert.js。小结assert.called是 Sinon 断言体系里最朴素、最常用的一环它把目标替身是否被调用过这一高频测试诉求封装成一个失败消息清晰、行为可预期的断言。理解它的语义边界只验证至少一次、底层数据来源proxy.called与incrementCallCount以及同族断言calledOnce、notCalled、calledWith等的分工能帮助你在编写单元测试时快速选出正确的断言写出既严谨又易读的测试代码。更多断言方法与使用示例可继续阅读 Assertions API 索引 及仓库中的 断言测试目录。赞分享测试开发工具【免费下载链接】sinonTest spies, stubs and mocks for JavaScript.项目地址https://gitcode.com/gh_mirrors/si/sinon点击查看免费下载相关推荐notebooklm-py 的 Android gRPC 能力与签名取证体系APK 静态提取、Web 端签名推断与移动端实证验证notebooklm py 的 Android gRPC 能力与签名取证体系APK 静态提取、Web 端签名推断与移动端实证验证 本文解析 notebookl测试开发工具Sinon assert.calledThrice 详解精确断言 spy/fake/stub 恰好被调用三次Sinon assert.calledThrice 详解精确断言 spy/fake/stub 恰好被调用三次 sinon.assert.calledThric测试开发工具Sinon assert.alwaysCalledWith 完全指南验证 fake/spy/stub 每次调用参数一致Sinon assert.alwaysCalledWith 完全指南验证 fake/spy/stub 每次调用参数一致 sinon.assert.always测试开发工具上一篇如何在Krita中3分钟实现AI绘画免费开源的终极创作神器下一篇3个资源捕获痛点猫抓浏览器扩展如何重新定义网页媒体下载体验创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
延伸阅读

更多相关文章

2026/9/24 17:21:40

进销存软件排行榜:2026年10款主流软件横评与选型

摘要:进销存软件用得对不对,直接影响开单、库存和对账效率。本文按开单顺不顺、库存准不准、对账清不清、价格合不合理四件事,横评10款主流产品,并给出不同业态的选型建议和上手四步。一、进销存排行榜是怎么排的?先看…

2026/9/23 12:07:00

GAMP 5 基于风险的计算机化系统验证:软件分类与审计追踪实践

简介:《A Risk-Based Approach to Compliant GxP Computerized Systems》即业内熟知的GAMP 5指南,面向制药企业质量与IT合规人员、验证工程师及计算机化系统管理者,用于解决GxP法规环境下系统合规性难以科学落地的问题。文档以风险管理为主线…

2026/9/23 12:06:55

安全托管MSSP实战:从静态防御到人机协同的攻防运营与应急响应

简介:这份PPT围绕互联网业务安全托管服务展开,面向企业安全负责人、IT运维人员及关注MSSP/MSS选型的读者,重点回应传统安全过度依赖人工、碎片化静态防御难以对抗产业化攻击等痛点。资源共1个pptx文件,包体约30.63MB,以…

2026/9/24 0:00:21

基于YOLOv8的渔船作业监控系统:从环境搭建到边缘部署全流程

简介:这是一套面向计算机、人工智能、自动化等专业学生与教师的毕业设计级项目资源,围绕YOLOv8实现渔船作业监控系统,可用于毕设、课程设计、大作业或项目立项演示。压缩包共97个文件,约24.21MB,以70个Python源码文件为…

2026/9/24 0:00:21

单细胞注释实战:基于Scanpy的标记基因与参考映射流程解析

简介:一份基于单细胞RNA测序数据的细胞类型注释算法研究Python毕业设计源码,针对计算机相关专业正在做毕设或需要项目实战的学习者,可用于课程设计与期末大作业。项目代码完整、经导师指导评审通过,可直接运行,覆盖数据…

2026/9/24 0:00:21

C#源生成器实战:用增量生成器替代反射,告别AOT崩溃

第一次在项目里被反射卡住,是在一个老旧的WinForms模块里:几十个类依赖PropertyChanged通知,运行时反射读属性、发通知,每次启动慢半拍不说,一上.NET Native/AOT裁剪模式几乎全面崩盘。后来我把这段逻辑全部改成C#源生…

2026/9/22 16:34:32

USB Type-C PCB布局分区设计:电源、高速信号与PD协议全攻略

做硬件这行,Type-C接口算是典型的“看着简单,做起来全坑”的东西。光引脚就24个,高低速信号、电源、控制线全部塞在一个小小的连接器里,如果PCB布局不做规划,打样回来基本就是“插上没反应”、“高速掉线”、“静电一打…

2026/9/22 20:01:30

系统编程学习原型如何补齐稳定性边界

系统编程学习原型如何补齐稳定性边界预算有限时&#xff0c;我先优化明显多余的复制&#xff0c;而不是猜测性地换容器。用借用传递只读数据通常就能减少分配&#xff1a; fn parse(line: &str) -> Result<Item, Error> { /* ... */ }用基准确认热点确实在分配&am…

2026/9/22 13:25:41

雨花区哪家财务公司代理记账比较好?

在雨花区&#xff0c;企业处理财税事务常常面临诸多挑战&#xff0c;选择一家靠谱的财务公司至关重要。湖南巨勤财务管理咨询有限公司就是本地正规实体财税服务机构&#xff0c;深耕本地工商财税行业多年&#xff0c;熟悉当地工商局、税务局最新政策与申报流程。主营公司注册、…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
咨询二维码