FEATURED · 精选文章

always 修饰符的秘密:用 sinon-chai 验证每一次调用都符合预期

发布时间 / 2026/8/21 17:53:25
来源 / 创域科博编辑部
栏目 / 资讯中心
always 修饰符的秘密:用 sinon-chai 验证每一次调用都符合预期 always 修饰符的秘密用 sinon-chai 验证每一次调用都符合预期【免费下载链接】sinon-chaiExtends Chai with assertions for the Sinon.JS mocking framework.项目地址: https://gitcode.com/gh_mirrors/si/sinon-chai在 JavaScript 单元测试中sinon-chai是为 Chai 断言库扩展Sinon.JS模拟框架断言的流行插件而always修饰符则是它最容易被忽视、也最强大的功能之一。很多人知道用calledWith检查 spy 的参数却不知道默认情况下它只要求至少一次命中。本文为你揭开 always 修饰符的秘密学会验证 spy 的每一次调用都符合预期写出更严谨可靠的测试。sinon-chai 是什么给 Chai 装上 Sinon.JS 的断言能力sinon-chai 的核心价值在于把 Sinon.JS 的 spy、stub、mock 断言无缝接入 Chai 的should与expect接口。对比一下两种写法原生 Sinon 断言sinon.assert.calledWith(mySpy, foo);使用 sinon-chai 之后mySpy.should.have.been.calledWith(foo); expect(mySpy).to.have.been.calledWith(foo);声明式的语法让测试意图一目了然。整个插件的实现非常轻量核心逻辑全部集中在 lib/sinon-chai.js 这一个文件中所有断言的映射关系也整理在 README.md 的表格里。always 修饰符的秘密从至少一次到每一次先看一个关键区别没有 always 时calledWith只要 spy 的某一次调用符合参数就通过加上 always 后则要求每一次调用都必须符合。const spy sinon.spy(); spy(bad); // 第一次调用参数不对 spy(good); // 第二次调用参数正确 // ✅ 通过只要存在一次正确调用 spy.should.have.been.calledWith(good); // ❌ 失败要求每一次调用都传入了 good spy.should.always.have.been.calledWith(good);这正是 always 修饰符的秘密所在——它是被大多数测试新手忽略的严格模式。上面的对比例子就来自项目的核心测试 test/callArguments.js你在测试先错后对的场景时会看到两种完全不同的结果。两种断言的判定对比测试场景calledWithalways.calledWithspy 从未被调用❌ 失败❌ 失败唯一一次调用参数正确✅ 通过✅ 通过第一次参数错误、第二次正确✅ 通过❌ 失败每次参数正确但多传了参数✅ 通过✅ 通过always 的三种等价写法链式顺序竟然可以随意调换always作为 Chai 断言链上的一个属性位置非常灵活以下三种写法完全等价spy.should.always.have.been.calledWith(good); spy.should.have.always.been.calledWith(good); spy.should.have.been.always.calledWith(good);实现原理在 lib/sinon-chai.jsalways只是一个设置标志位的属性随后在 lib/sinon-chai.js 的处理器中当检测到该标志位且 spy 存在对应的alwaysXxx方法时会自动切换到严格版本。这也是为什么should.have.been.alwaysCalledWith(...)这种写法不生效——always 必须作为链上的一个环节而不是方法名的一部分。always 修饰符的适用范围参数、this、返回值、异常全覆盖always 不是calledWith的专属它几乎能作用于所有 Sinon spy 断言sinon-chai 断言对应的 Sinon.JS 方法验证内容always.calledWithalwaysCalledWith每次调用都包含指定参数always.calledWithExactlyalwaysCalledWithExactly每次调用的参数完全一致不多不少always.calledWithMatchalwaysCalledWithMatch每次调用都匹配指定匹配器always.calledOnalwaysCalledOn每次调用的this都是指定上下文always.calledWithNewalwaysCalledWithNew每次调用都通过new触发always.returnedalwaysReturned每次调用都返回指定值always.thrownalwaysThrew每次调用都抛出指定异常例如验证一个函数每次都被new调用测试见 test/callingWithNew.jsnew spy(); spy(); // 这次没有用 new spy.should.have.been.calledWithNew; // ✅ 至少有一次用了 new spy.should.always.have.been.calledWithNew; // ❌ 并非每次都用 new再比如验证每次调用的返回值测试见 test/returning.jsconst spy sinon.spy((n) n * 2); spy(1); // 返回 2 spy(3); // 返回 6 spy.should.always.have.returned(2); // ❌ 第二次返回了 6always 与 callCount两种严格验证的搭配思路有些同学容易把always和callCount混为一谈其实它们解决的是不同维度的需求callCount(n)验证 spy被调用的总次数属于数量维度always.xxx验证每一次调用的质量参数、this、返回值属于内容维度。两者经常组合使用先确认调用次数再确认每次的调用内容双保险const spy sinon.spy(); spy(1); spy(1); spy(1); spy.should.have.callCount(3); spy.should.always.have.been.calledWith(1);如果某次调用参数不一致callCount是发现不了的而always.calledWith能立刻抓住它——这正是 always 修饰符不可替代的价值。实战案例用 always 抓住隐藏的调用缺陷假设你写了一个带缓存的工具函数并希望验证回调每次都被正确调用function fetchData(url, cb) { if (cache[url]) { cb(cache[url]); // 命中缓存 } else { fetch(url).then((data) { cache[url] data; cb(data); // 首次请求 }); } }如果只想验证回调被调用过calledWith就够用但若要保证缓存分支和网络分支返回的数据格式一致就必须用alwaysfetchData(/api, cb); fetchData(/api, cb); // 命中缓存 expect(cb).to.always.have.been.calledWithMatch({ code: 200 });这样即使某一次调用返回了错误结构测试也会立刻失败而不是被至少一次命中掩盖过去。新手常见误区与最佳实践误以为 calledWith 会检查所有调用这是最常见的坑默认它只要求存在一次匹配需要严格验证时务必加always。把 always 写进方法名alwaysCalledWith是 Sinon 的 APIsinon-chai 里请写成always.have.been.calledWith的链式形式。一次调用场景优先用 calledOnceWith如果 spy 只应该被调用一次直接用calledOnceWith/calledOnceWithExactly更明确测试见 test/callArguments.js。配合.not反向断言spy.should.not.always.have.been.calledWith(bad)等价于至少有一次不是坏参数语义清晰。如何安装并开始使用 sinon-chai安装非常简单一行命令即可npm install --save-dev sinon-chai然后在测试入口注册插件import * as chai from chai; import sinonChai from sinon-chai; chai.use(sinonChai);如果你想阅读源码或动手贡献也可以克隆仓库git clone https://gitcode.com/gh_mirrors/si/sinon-chai项目体积小巧非常适合作为学习 Chai 插件开发的范本。小结always修饰符是 sinon-chai 中验证每一次调用都符合预期的关键武器。从参数到返回值从this到异常它都能帮你把断言从至少一次升级为每一次让隐藏的边界缺陷无所遁形。下次写测试时记得问自己一句这一次我是不是该加上 always【免费下载链接】sinon-chaiExtends Chai with assertions for the Sinon.JS mocking framework.项目地址: https://gitcode.com/gh_mirrors/si/sinon-chai创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
RELATED — 相关阅读

相关资讯

LATEST — 最新资讯

最新发布

TODAY — 本日精选

新闻

WEEKLY — 本周精选

新闻

MONTHLY — 本月精选

新闻