测试 this 上下文与 new 调用:sinon-chai 的 calledOn 与 calledWithNew 指南

📅 2026/8/21 18:08:53
测试 this 上下文与 new 调用:sinon-chai 的 calledOn 与 calledWithNew 指南
测试 this 上下文与 new 调用sinon-chai 的 calledOn 与 calledWithNew 指南【免费下载链接】sinon-chaiExtends Chai with assertions for the Sinon.JS mocking framework.项目地址: https://gitcode.com/gh_mirrors/si/sinon-chaiJavaScript 的this指向和new调用是单元测试里最容易踩坑、也最容易被忽视的两个细节。sinon-chai作为 Chai 生态中广受欢迎的断言插件把 Sinon.JS 的 spy、stub 能力与 Chai 的自然语言断言合二为一其中calledOn与calledWithNew正是专门用来验证调用方式的利器。本指南将带你用最短时间掌握 this 上下文测试与 new 调用测试的完整写法让你的测试代码更严谨、更易读。为什么单元测试要验证 this 上下文与 new 调用先看两个真实场景this 指向错误某个方法内部依赖this访问实例数据调用方却用了解构或单独引用的方式调用导致this丢失功能静默失效。忘记 new 调用构造函数被当成普通函数调用this会指向全局对象严格模式下为undefined初始化逻辑全部落空还很难定位。这类 bug 往往不在参数对不对上暴露因此常规的calledWith断言无能为力。sinon-chai 的 calledOn 断言与 calledWithNew 断言正是为这两种场景量身定制。快速上手安装与初始化 sinon-chai 插件一行命令即可完成安装npm install --save-dev sinon-chai然后在测试入口文件Mocha 等测试框架的 fixture 中完成初始化import * as chai from chai; import sinonChai from sinon-chai; chai.use(sinonChai); chai.should(); // 如果打算使用 should 风格如果你希望阅读或修改源码也可以直接克隆仓库git clone https://gitcode.com/gh_mirrors/si/sinon-chaicalledOn 基本用法断言 this 指向指定对象calledOn(context)用于验证 spy 在调用时this是否指向你期望的对象。它同时支持expect与should两种风格it(should be called with target as this, function () { const spy sinon.spy(); const target { name: 目标对象 }; spy.call(target); // 通过 call 显式指定 this expect(spy).to.have.been.calledOn(target); // ✅ 通过 // spy.should.have.been.calledOn(target); // should 风格同样可用 });几个值得注意的细节只要有一次满足即可通过spy 被多次调用时只要其中任意一次this正确calledOn就通过。可以针对单次调用断言使用spy.getCall(n)精确检查第 n 次调用例如spy.getCall(1).should.have.been.calledOn(target)。对 spy、stub、mock 及单个 call 对象全部生效这也是 sinon-chai 的通用设计。calledWithNew 用法断言函数通过 new 调用calledWithNew用于验证 spy 是否以new关键字调用是检测忘记 new这类问题的首选断言。特别注意它是属性property而非方法所以后面不要加括号it(should be called with new, function () { const spy sinon.spy(); new spy(); // 通过 new 调用 expect(spy).to.have.been.calledWithNew; // ✅ 正确写法无括号 // ❌ 错误写法expect(spy).to.have.been.calledWithNew(); });如果你确实用了new而断言失败大概率就是多了那一对括号——这是新手最常踩的坑。同样地当 spy 被多次调用时只要存在一次new调用该断言即通过。always 断言严格要求每一次调用都符合条件普通断言只要求至少一次符合而always变体要求每一次调用都满足条件非常适合对行为一致性要求严格的场景spy.call(target); spy.call(target); spy.should.always.have.been.calledOn(target); // ✅ 两次 this 都正确new spy(); new spy(); spy.should.always.have.been.calledWithNew; // ✅ 每次都是 new 调用⚠️ 注意always的位置必须紧跟在should之后写作should.always.have.been.calledOn(...)而不能写成should.have.been.alwaysCalledOn(...)。反向断言与错误消息调试不再迷茫所有断言都可以用 Chai 的.not取反spy.should.not.have.been.calledOn(target); spy.should.not.have.been.calledWithNew;当断言失败时sinon-chai 会给出清晰的中文可读错误信息直接告诉你期望是什么、实际是什么断言失败时的典型错误消息calledOnexpected spy to have been called with { } as this, but it was called with ... insteadcalledWithNewexpected spy to have been called with newalways.calledOnexpected spy to always have been called with { } as this, ...有了这样的提示配合getCall(n)逐次排查定位问题往往只需几秒钟。两个断言对比速查表对比项calledOn(context)calledWithNew验证目标this 上下文指向是否通过 new 调用是否带参数✅ 需要传入 context❌ 无参数是否加括号✅ 方法带括号❌ 属性无括号always 变体always.have.been.calledOn(ctx)always.have.been.calledWithNew对应实现lib/sinon-chai.js第 171-175 行lib/sinon-chai.js第 166 行从哪里看源码与测试用例想深入理解这两个断言的实现与边界行为可以直接阅读项目源码核心实现lib/sinon-chai.js其中always标志、断言消息生成逻辑都在此文件中calledOn与calledWithNew的注册代码清晰可读。测试用例test/callContext.js覆盖了 calledOn 的通过、失败、多次调用、always 等全部场景test/callingWithNew.js则完整验证了 calledWithNew 的行为边界。错误消息断言test/messages.js中 about call context 与 about calling with new 两个小节展示了断言失败时的精确消息格式。小结calledOn与calledWithNew是 sinon-chai 插件中验证调用方式的一对黄金组合前者守护this上下文后者拦截忘记 new的隐患。记住三个要点即可calledOn(target)是方法需要传参calledWithNew是属性不要加括号。需要每一次都满足时使用should.always.have.been.xxx形式。结合.not取反与getCall(n)单次断言可覆盖绝大多数边界场景。把它们用进你的测试套件JavaScript 中关于调用方式的隐性问题从此无处遁形。【免费下载链接】sinon-chaiExtends Chai with assertions for the Sinon.JS mocking framework.项目地址: https://gitcode.com/gh_mirrors/si/sinon-chai创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考