news 2026/8/21 18:08:51

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

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
测试 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-chai

JavaScript 的this指向和new调用,是单元测试里最容易踩坑、也最容易被忽视的两个细节。sinon-chai作为 Chai 生态中广受欢迎的断言插件,把 Sinon.JS 的 spy、stub 能力与 Chai 的自然语言断言合二为一,其中calledOncalledWithNew正是专门用来验证调用方式的利器。本指南将带你用最短时间掌握 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-chai

calledOn 基本用法:断言 this 指向指定对象

calledOn(context)用于验证 spy 在调用时,this是否指向你期望的对象。它同时支持expectshould两种风格:

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 ... instead
calledWithNewexpected spy to have been called with new
always.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标志、断言消息生成逻辑都在此文件中,calledOncalledWithNew的注册代码清晰可读。
  • 测试用例:test/callContext.js覆盖了 calledOn 的通过、失败、多次调用、always 等全部场景;test/callingWithNew.js则完整验证了 calledWithNew 的行为边界。
  • 错误消息断言:test/messages.js中 "about call context" 与 "about calling with new" 两个小节,展示了断言失败时的精确消息格式。

小结

calledOncalledWithNew是 sinon-chai 插件中验证调用方式的一对黄金组合:前者守护this上下文,后者拦截"忘记 new"的隐患。记住三个要点即可:

  1. calledOn(target)是方法,需要传参;calledWithNew是属性,不要加括号。
  2. 需要"每一次都满足"时,使用should.always.have.been.xxx形式。
  3. 结合.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),仅供参考

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

2026年Java面试趋势:云原生、AI与分布式系统

1. 2026年Java面试趋势与核心考察点 2026年的Java技术栈面试呈现出三个显著变化:云原生深度整合、AI工程化能力要求、分布式系统设计精细化。根据近半年一线大厂的实际面试反馈,以下是最新考察重点: Java 21新特性实战 : 虚拟线…

作者头像 李华
网站建设 2026/8/21 18:06:48

kolpa时间与User Agent生成技巧:3个方法打造真实感数据

kolpa时间与User Agent生成技巧:3个方法打造真实感数据 【免费下载链接】kolpa A fake data generator written in and for Go 项目地址: https://gitcode.com/gh_mirrors/ko/kolpa kolpa 是一个用 Go 语言编写、专为 Go 开发者打造的假数据生成器&#xff0…

作者头像 李华
网站建设 2026/8/21 17:59:54

QHotkey macOS实现原理:Carbon API注册全局热键的完整流程

QHotkey macOS实现原理:Carbon API注册全局热键的完整流程 【免费下载链接】QHotkey A global shortcut/hotkey for Desktop Qt-Applications 项目地址: https://gitcode.com/gh_mirrors/qh/QHotkey 对于桌面应用开发者来说,QHotkey 是一个非常有…

作者头像 李华