首页/新闻资讯/正文详情

Sinon 断言 `assert.neverCalledWithMatch` 深入解析:验证 fake/spy/stub 从未以“匹配”参数被调用

发布时间:2026/9/24 20:24:04 来源:云帆数科 栏目:资讯中心
Sinon 断言 `assert.neverCalledWithMatch` 深入解析:验证 fake/spy/stub 从未以“匹配”参数被调用
Sinon 断言assert.neverCalledWithMatch深入解析验证 fake/spy/stub 从未以“匹配”参数被调用【免费下载链接】sinonTest spies, stubs and mocks for JavaScript.项目地址: https://gitcode.com/gh_mirrors/si/sinonsinon.assert.neverCalledWithMatch(spy, arg1, arg2, ...)是 Sinon 内置断言 API 中的“否定 模糊匹配”组合用于验证一个fake、spy或stub从未以符合给定匹配条件partial match部分匹配的参数被调用。它适合在回调参数为对象、且你只关心其中几个字段时使用——例如确认某个副作用函数从未收到“类型为 Apple pie”的 payload。读完本文你将掌握该断言的语义、底层实现调用链、与严格匹配断言neverCalledWith的差异以及如何在测试框架中落地使用。API 签名与核心语义sinon.assert.neverCalledWithMatch(spy, arg1, arg2, ...);当spy这里的spy泛指 fake / spy / stub从未被“匹配”给定参数地调用时断言通过不抛错一旦存在任意一次调用满足匹配条件断言失败并抛出AssertError。该断言的官方定义明确给出了它与严格匹配断言之间的等价关系This behaves the same way assinon.assert.neverCalledWith(spy, sinon.match(arg1), sinon.match(arg2), ...)。也就是说neverCalledWithMatch本质上等价于把每个期望参数用sinon.match包裹后再调用sinon.assert.neverCalledWith。区别在于断言匹配方式典型场景neverCalledWith深度严格相等deepEqual期望参数完全精确时neverCalledWithMatchpartial match对象只要求包含指定属性且支持嵌套 matcher只关心参数的部分结构或类型特征时文档示例对象的 partial match官方文档给出了一个非常直观的示例——fake被调用过两次但只有第二次传入的对象包含了期望的name: apple pie属性import * as sinon from sinon; const fake sinon.fake(); const applePieExpectation { name: apple pie }; fake({ name: cherry pie, price: 123 }); // No match, no error :) sinon.assert.neverCalledWithMatch(fake, applePieExpectation); fake({ name: apple pie, price: 123 }); sinon.assert.neverCalledWithMatch(fake, applePieExpectation); // Uncaught: Error [AssertError]: expected fake to never be called with match { name: apple pie } // fake({ name: cherry pie, price: 123 }) at REPL10:1:1 // fake({ name: apple pie, price: 123 }) at REPL16:1:1注意两点partial match 语义第一次调用fake({ name: cherry pie, price: 123 })时参数对象里没有name: apple pie属性因此不构成匹配断言通过失败信息可读性第二次调用fake({ name: apple pie, price: 123 })包含了期望属性构成匹配断言抛出AssertError错误信息以expected fake to never be called with match { name: apple pie }开头并附上所有历史调用的日志%C调用记录方便定位是哪一次调用“污染”了断言。源码级实现断言如何落到每一次调用记录上neverCalledWithMatch并不是一个独立手写的断言而是通过源码中三个层次协作完成的1. 断言层mirrorPropAsAssertion镜像代理方法在 src/sinon/assert.js 中该断言由工厂函数mirrorPropAsAssertion生成mirrorPropAsAssertion( neverCalledWithMatch, expected %n to never be called with match %*%C, );mirrorPropAsAssertionsrc/sinon/assert.js的工作方式是先对传入的fake调用verifyIsStub校验其确为 spy/stub 代理然后把剩余参数透传给fake上的同名方法fake.neverCalledWithMatch(...)根据返回值判断成败失败时调用failAssertion抛出错误错误对象name被设置为AssertError见 src/sinon/assert.js。2. 代理层delegateToCalls把断言分发给每次调用在 src/sinon/proxy.js 中spy 代理对象通过delegateToCalls将neverCalledWithMatch委托给每次调用的notCalledWithMatch并提供一个默认回退delegateToCalls( proxyApi, neverCalledWithMatch, false, notCalledWithMatch, false, function () { return true; }, );delegateToCalls会把查询遍历到每一次spyCall上只要任意一次调用不匹配最终结果即失败而最后一个参数返回true的回退函数表示当 spy 从未被调用过时默认判定为“通过”——因为没有任何一次调用满足匹配这符合“never called with match”的字面语义。3. 调用记录层notCalledWithMatch取反calledWithMatch在 src/sinon/proxy-call.js 中单次调用记录上的实现非常简洁notCalledWithMatch: function notCalledWithMatch() { return !this.calledWithMatch.apply(this, arguments); },而calledWithMatchsrc/sinon/proxy-call.js是匹配的核心它逐一用samsam的createMatcher构造匹配器再对expectation.test(actual)做reduce聚合若期望参数个数多于实际调用参数个数直接判定不匹配。从源码结构看整条链路为assert.neverCalledWithMatch→fake.neverCalledWithMatch→ 逐次调用call.notCalledWithMatch→!call.calledWithMatch→sinon.match(expectation).test(actual)从而实现了“从未以匹配参数被调用”的判定。与sinon.match匹配器组合从“部分对象”到“任意复杂条件”由于neverCalledWithMatch等价于对每个参数套上sinon.match(...)它天然支持sinon.match的全部能力sinon.match(string)要求值是字符串且包含期望子串sinon.match(regexp)要求字符串匹配正则sinon.match(object)要求值非null/undefined且至少包含期望对象的全部属性支持嵌套匹配器{ id: sinon.match.number }sinon.match(function)使用自定义匹配器见 custom matchers以及sinon.match.any、sinon.match.array、sinon.match.number、sinon.match.string等内置类型匹配器完整列表见 matchers API。例如验证fake从未以“任意字符串”作为唯一参数被调用import * as sinon from sinon; const fake sinon.fake(); fake(123); // 123 不是字符串matcher 不匹配 → 断言通过 sinon.assert.neverCalledWithMatch(fake, sinon.match.string);这正是 docs/tests/docs/assertions/api/never-called-with-match.test.js 中第二个用例验证的场景。在测试框架中使用含完整测试用例文档中的示例测试对应仓库测试文件 docs/tests/docs/assertions/api/never-called-with-match.test.js使用tap编写覆盖了通过、matcher 不匹配、失败三种典型情况可直接复制到任意基于 Node 的测试环境import tap from tap; import * as sinon from sinon; tap.test( assert.neverCalledWithMatch - passes when not called with matching arguments, (t) { const fake sinon.fake(); fake({ name: Bob }); t.doesNotThrow(() { sinon.assert.neverCalledWithMatch(fake, { name: Alice }); }, assertion should pass); t.end(); } ); tap.test( assert.neverCalledWithMatch - passes with matcher that doesnt match, (t) { const fake sinon.fake(); fake(123); t.doesNotThrow(() { sinon.assert.neverCalledWithMatch(fake, sinon.match.string); }, assertion should pass when matcher doesnt match); t.end(); } ); tap.test( assert.neverCalledWithMatch - fails when called with matching arguments, (t) { const fake sinon.fake(); fake({ name: Alice, age: 30 }); t.throws( () sinon.assert.neverCalledWithMatch(fake, { name: Alice }), /expected fake to never be called with match/, assertion should fail when called with matching arguments ); t.end(); } );关键断言点用t.doesNotThrow验证“没有匹配调用”时断言静默通过用t.throws 正则/expected fake to never be called with match/验证失败场景的错误信息格式保证失败信息对调试者友好。如果你使用的是 Mocha / Jest / Vitest 等框架只需把t.doesNotThrow/t.throws换成对应框架的expect(...).not.toThrow()/expect(...).toThrow()即可断言本身与框架无关。失败时的错误信息与调试支持断言失败时抛出的错误具有两个对调试至关重要的特性错误类型错误对象名为AssertErrorerror.name AssertError见 src/sinon/assert.js方便在测试运行器中做统一的错误归类与展示消息内容格式串为expected %n to never be called with match %*%C其中%n替换为 spy 名称未命名时为fake%C展开为全部历史调用记录含每个调用发生的位置如文档示例中的at REPL10:1:1因此一旦断言失败你立刻能看到“是哪一次调用命中了匹配条件”。此外断言对象支持通过createAssertObject定制日志输出行为src/sinon/assert.jsshouldLimitAssertionLogs默认false与assertionLogLimit默认10000可用于在调用记录极多时截断错误消息避免终端被刷屏。与其他断言的对比与选择neverCalledWithMatch是 Sinon 断言家族“否定侧”的模糊匹配成员与它最常被一起权衡的兄弟方法有assert.neverCalledWith严格相等版本要求参数完全一致expected fake to never be called with arguments apple pie。当你不确定对象全部字段、只想盯住关键字段时应改用neverCalledWithMatchassert.calledWithMatch同一匹配语义的“肯定侧”版本并支持针对单个spyCall断言sinon.assert.calledWithMatch(spy.secondCall, arg1, ...)正向约束族calledOnceWithMatch恰好一次且匹配、alwaysCalledWithMatch所有调用均匹配与“从未匹配”构成完整的正向/负向覆盖spy 实例方法spy.neverCalledWithMatchspies API与单次调用记录的spyCall.notCalledWithMatchspy-call API——sinon.assert.neverCalledWithMatch底层正是复用这些方法并叠加断言校验与格式化错误消息。完整断言方法清单见 Assertions API 索引。最佳实践小结优先用于“防误入”类负向验证如“缓存回调绝不能被以type: internal的请求触发”“上报函数绝不能收到含name: apple pie的 payload”用 partial match 降低脆弱性对象参数带上price等无关字段的精确全量断言会让测试与实现过度耦合neverCalledWithMatch只关注必要字段失败信息即文档配合AssertError与调用记录输出断言失败时能直接从消息中定位命中匹配的具体调用与正向断言互补把calledWithMatch应发生与neverCalledWithMatch不应发生成对使用可完整刻画一个函数的参数契约。【免费下载链接】sinonTest spies, stubs and mocks for JavaScript.项目地址: https://gitcode.com/gh_mirrors/si/sinon创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

相关推荐

基于SpringBoot+Vue的高校就业管理系统设计与实现
基于SpringBoot+Vue的高校就业管理系统设计与实现

1. 毕设选题复盘:为什么我敲定了高校就业管理系统每年到了毕设开题季,大批计算机专业的学生就开始在“图书管理系统”“商城系统”“酒店管理系统”里反复横跳。说实话,这几个方向已经被做到快烂大街了,答辩现场撞题率极高&#x… · 2026/9/24 20:23:58

抖音视频只推荐一次?深度解析前置审核机制与流量分发逻辑
抖音视频只推荐一次?深度解析前置审核机制与流量分发逻辑

很多做抖音的朋友都经历过这种场景:精心剪了一下午的视频发出去,隔一小时看一次播放量,数字像钉在墙上一样纹丝不动,到最后只看到孤零零的一个推荐,平台像是把你的内容扔进了一个没人的角落,再也没多给过一… · 2026/9/24 20:23:58

用MATLAB实现电晕放电电场仿真与数值分析
用MATLAB实现电晕放电电场仿真与数值分析

电晕放电这个词,听起来像是高电压专业才会碰到的冷门概念,但只要你接触过高压输电、绝缘设计、静电除尘,甚至只是做过高压实验,就一定绕不开它。简单说,电晕放电是导体表面电场强度超过空气击穿场强时,周围… · 2026/9/24 20:23:58

方向数组dx/dy算法实战:从迷宫BFS到贪吃蛇模拟的完整指南
方向数组dx/dy算法实战:从迷宫BFS到贪吃蛇模拟的完整指南

迷宫与地图模拟:完整体验方向数组 dx/dy 的进阶练法如果你上过几次算法课,大概已经被“方向数组”这个词听过不下十遍了。它简单到不行——就是用int dx[4] {-1, 1, 0, 0};配合int dy[4] {0, 0, -1, 1};表示上下左右四个方向,再配合循环来访… · 2026/9/24 21:00:13

免费字幕软件与视频转文字工具推荐:从语音识别到成片字幕的完整流程
免费字幕软件与视频转文字工具推荐:从语音识别到成片字幕的完整流程

做视频这几年,字幕这一块我踩过的坑比调色还多。刚开始我以为字幕就是把打好的字往时间轴上一拖,直到一条四十分钟的口播视频让我手动打轴打了一个通宵,我才真正意识到,字幕在视频制作里的分量有多重。后来陆陆续续换过几十款免费… · 2026/9/24 21:00:13

独立开发者的技术选型指南:六维度决策框架与实战避坑
独立开发者的技术选型指南:六维度决策框架与实战避坑

说实话,干独立开发这几年,我发现最折磨人的不是写代码、调Bug,而是做技术决策。从“要不要引入这个框架”到“这个项目到底用不用得上微服务”,每一个选择都在消耗时间、精力,还有最宝贵的机会成本。早些年我特别喜欢追… · 2026/9/24 21:00:13

方向数组(dx/dy)与地图模拟:五道课后习题全拆解
方向数组(dx/dy)与地图模拟:五道课后习题全拆解

第五课的方向数组(dx/dy)和地图模拟,课上听着很简单,无非就是两个数组加一个循环。但真正落到课后习题上,很多同学会卡在莫名其妙的 bug 上:要么数组越界程序崩溃,要么走进死循环出不来&#xf… · 2026/9/24 21:00:13

用GPT-6 Astra一天打造家庭数字孪生:从量房到Unity实时可视化
用GPT-6 Astra一天打造家庭数字孪生:从量房到Unity实时可视化

我家户型图不算复杂,但真要一砖一瓦建模再写一套数据看板,以前想都不敢想。直到我拿到 GPT-6 Astra 的测试资格,萌生了一个大胆的想法:能不能让这个多模态 AI 帮我一天之内把家做成数字孪生?结果还真成了——从早上九点… · 2026/9/24 21:00:00

YOLOv5+DeepSORT多目标跟踪实战:从检测到去重计数与轨迹分析
YOLOv5+DeepSORT多目标跟踪实战:从检测到去重计数与轨迹分析

简介:这份资源面向计算机视觉初学者与进阶开发者,聚焦车辆与行人追踪计数这一典型落地场景,提供基于YOLOv5与DeepSORT的完整项目实践代码。YOLOv5负责实时目标检测,输出带置信度与分类标签的边界框;DeepSORT则借助卡尔… · 2026/9/24 21:00:00

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

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

1D-CNN时间序列建模实战:从Conv1d原理到工业落地
1D-CNN时间序列建模实战:从Conv1d原理到工业落地

简介:面向时间序列数据建模的一维卷积神经网络完整实现,适合深度学习入门者及需要快速验证时序模型的研究者,能够从音频、文本、传感器或股价等序列中挖掘局部特征与时间依赖。压缩包体积很小,只有3KB,内含3个Python脚… · 2026/9/24 0:00:26

柔软的L:汉语语流中被忽视的舌肌张力控制
柔软的L:汉语语流中被忽视的舌肌张力控制

1. 这个“L”不是字母表里的L,而是舌尖上的L最近在几个方言群和语音教学社群里,反复看到有人发一句:“也说字母L:柔软的长舌”。初看以为是英语发音课笔记,点开才发现全是方言爱好者、播音系学生、语言康复师甚至戏曲演… · 2026/9/24 0:00:44

了解更多?预约专属演示

我们的顾问将为您一对一讲解产品与方案

企业微信二维码