深入理解 Sinon 的 `spyCall.firstArg`:读取单次调用首个参数的正确姿势
测试开发工具【免费下载链接】sinonTest spies, stubs and mocks for JavaScript.项目地址https://gitcode.com/gh_mirrors/si/sinon点击查看免费下载spyCall.firstArg是 Sinon 中 spy call 对象的一个核心只读属性用于获取某一次函数调用传入的第一个参数值。它适用于 fake、spy、stub 和 mock 方法等所有被 Sinon 代理过的函数是细粒度验证调用行为、提取回调参数时最常用的入口之一。读完本文你将掌握firstArg的语义边界含无参数调用时的表现、获取 spyCall 的多种途径以及它在源码中的实现原理。spyCall.firstArg是什么在 Sinon 的概念体系中spy call 是对一次“被监视函数调用”的对象化表示。当 fake、spy、stub 或 mock 方法 被调用时Sinon 会为每一次调用生成一个独立的 spyCall 对象其中就包含本次调用的全部细节args本次调用的参数数组firstArg本次调用的第一个参数本文主题lastArg本次调用的最后一个参数callback本次调用中作为回调的函数参数returnValue本次调用的返回值exception本次调用抛出的异常thisValue本次调用执行时的this上下文firstArg的官方定义非常简洁——它保存了对该次调用第一个参数的引用参见 first-arg.md。这里的“引用”一词值得注意它直接指向原始参数值本身而非拷贝因此对复杂对象如对象、数组、函数使用时比较的是引用与原始值。获取 spyCall 对象的五种途径要读取firstArg首先需要拿到某个 spyCall 对象。Sinon 提供了以下常用途径1.getCall(n)按索引取第 n 次调用fake.getCall(n)返回第n次调用的 spyCall索引从 0 开始。在 proxy.js 的实现中getCall还支持负数索引——-1表示从最后一次调用往前数const f sinon.fake(); f(a); f(b); f(c); f.getCall(0).firstArg; // a f.getCall(1).firstArg; // b f.getCall(-1).firstArg; // c倒数第一次调用2.firstCall/lastCall首尾调用快捷属性proxy-call-util.js 中的createCallProperties会在每次调用后更新代理上的firstCall、secondCall、thirdCall与lastCall属性const f sinon.fake(); f(apple pie, banana pie); f.lastCall.firstArg; // apple pie f.firstCall.firstArg; // apple pie3.getCalls()批量获取全部调用当需要遍历所有调用逐个检查firstArg时getCalls()返回所有 spyCall 的数组。4.secondCall/thirdCall特定序号调用与firstCall类似secondCall与thirdCall分别指向第二、第三次调用不存在时为null。5.this.args[0]从代理级参数数组取首参代理对象本身也有args所有调用的参数数组的数组f.args[0][0]等价于f.getCall(0).firstArg。但 spyCall 的firstArg语义更直观且不受args二维结构影响。基本行为有参调用与无参调用firstArg的取值规则由创建 spyCall 时的参数个数决定。参照 first-arg.test.js 中的官方测试import t from tap; import sinon from sinon; const f sinon.fake(); // 有参数调用firstArg 为第一个参数 f(apple pie, banana pie); t.equal(f.lastCall.firstArg, apple pie); // 通过 // 无参数调用firstArg 为 undefined f(); t.equal(f.lastCall.firstArg, undefined); // 通过两个关键结论有参数时firstArg精确等于args[0]无参数调用时firstArg为undefined不会抛出异常或返回null。这一点在 fakes 基础用法测试 中也有印证未传参数的 fake 调用后fake.firstArg为undefined。因此断言firstArg是否为undefined可以用来判断某次调用是否携带了参数。结合其他 spyCall 属性完成组合断言firstArg常常需要与args、lastArg等属性配合使用才能准确描述一次调用const spy sinon.spy(); spy(query, { page: 2 }, callback); const call spy.lastCall; call.firstArg; // query首个参数 call.args; // [query, { page: 2 }, callback]完整参数数组 call.args[0]; // 等价于 call.firstArg call.lastArg; // callback最后一个参数特别地lastArg与callback有关联在 proxy-call.js 的创建逻辑中若最后一个参数是函数它同时会成为lastArg和callback。结合 callback 属性文档 可进一步提取回调。在 fake、spy、stub 与 mock 上的统一表现由于 fake、spy、stub 与 mock 方法最终都经由同一套 proxy 机制创建调用记录proxy.js、proxy-invoke.jsfirstArg的行为在四种对象上完全一致// spy包装现有函数 const spy sinon.spy((name) hello ${name}); spy(world); spy.lastCall.firstArg; // world // stub预定义行为 const stub sinon.stub().returns(1); stub(x, y); stub.lastCall.firstArg; // x // fake独立假函数 const fake sinon.fake.returns(42); fake(1, 2); fake.lastCall.firstArg; // 1 // mock 方法 const obj { greet: () {} }; const mock sinon.mock(obj); const expectation mock.expects(greet); obj.greet(hi); expectation.lastCall.firstArg; // hiexpectation 本身也是代理源码级原理firstArg从何而来理解firstArg的底层实现能帮你预判各种边界情况。核心逻辑位于 proxy-call.js 的createProxyCall工厂函数export default function createProxyCall( proxy, thisValue, args, returnValue, exception, id, errorWithCallStack, ) { let firstArg, lastArg; if (args.length 0) { firstArg args[0]; lastArg args[args.length - 1]; } const proxyCall Object.create(callProto); // ... proxyCall.args args; proxyCall.firstArg firstArg; proxyCall.lastArg lastArg; // ... return proxyCall; }由此可以明确实现事实firstArg并非 getter 而是普通实例属性在 spyCall 创建时一次性写入它直接取自调用时记录的args[0]因此是对原参数的引用当args.length 0时firstArg保持undefined局部变量未赋值不会额外处理同理lastArg为args[args.length - 1]两者只在args非空时才有值。调用链完整回放可对照 proxy-invoke.js代理函数被调用invoke(func, thisValue, args)执行调用incrementCallCount并把args、thisValue、callId推入对应数组调用createCallProperties刷新firstCall/lastCall等快捷属性内部调用getCall执行原函数记录返回值/异常再次调用createCallProperties使 spyCall 携带完整的returnValue、exception等。因此每次访问f.lastCall.firstArg实际上都会经由getCall(this.callCount - 1)实时构建一个新的 spyCall 对象并读取其firstArg属性。典型实战场景场景一断言函数以特定参数被调用const sendEmail sinon.spy(); sendEmail(userexample.com, 欢迎注册, body); sinon.assert.calledWith(sendEmail, userexample.com); // 更细粒度直接检查最后一次调用的首个参数 t.equal(sendEmail.lastCall.firstArg, userexample.com);场景二从调用记录中提取回调参数const fake sinon.fake(); fake(1, 2, done); fake.lastCall.firstArg; // 1 —— 但若首参不是回调应改用 callback / lastArg注意firstArg只承诺“第一个参数”不保证它是函数。若需要提取回调应使用 callback 属性它指向最后一个函数参数或yield/yieldTo系列方法。场景三多参数调用中的位置敏感断言const f sinon.fake(); f(GET, /api/users, { limit: 10 }); const call f.getCall(0); call.firstArg; // GET call.args[1]; // /api/users call.args[2]; // { limit: 10 }注意事项与边界情况无参数调用返回undefined判断“是否携带参数”时请用firstArg ! undefined或检查args.length不要依赖 null的宽松判断undefined null为真会与显式传null混淆。显式传入undefined与不传参数不同f(undefined)的firstArg是undefined有参数但值为 undefined而f()的firstArg也是undefined无参数。两者在firstArg上无法区分需要时用args.length区分。与代理级firstArg的区别代理对象上也有firstArg属性初始为null见 proxy.js它记录的是最近一次调用的首参而spyCall.firstArg是某个特定调用的首参二者不要混用。多调用场景下请优先使用lastCall.firstArg或getCall(n).firstArg。重置后失效调用resetHistory或sinon.reset()后调用记录清空lastCall变为null此时访问null.firstArg会抛错应先判空参考 get-call.test.js 中getCall(1) null的断言。相关文档速查spy-call 概念总览spyCall 的定位与getCall说明spyCall API 索引全部方法与属性列表lastArg与firstArg对称的末参属性args完整参数数组spies 入门spy 的创建与使用fakes 入门fake 的创建与使用源码与测试参考proxy-call.jsspyCall 工厂、proxy.jsgetCall、proxy-call-util.js调用属性维护、first-arg.test.js官方行为测试。赞分享测试开发工具【免费下载链接】sinonTest spies, stubs and mocks for JavaScript.项目地址https://gitcode.com/gh_mirrors/si/sinon点击查看免费下载相关推荐Sinon 断言系列assert.notCalled 全解析——验证 fake/spy/stub 未被调用的正确姿势Sinon 断言系列 assert.notCalled 全解析——验证 fake/spy/stub 未被调用的正确姿势 assert.notCalled sp测试开发工具Sinon spy.alwaysCalledWithExactly 深入解析如何断言每次调用参数都精确一致Sinon spy.alwaysCalledWithExactly 深入解析如何断言每次调用参数都精确一致 Sinon 是 JavaScript 生态中最常用测试开发工具如何用 pascal update 升级 Pascal Editor 本地运行时并在失败时回滚到旧版本如何用 pascal update 升级 Pascal Editor 本地运行时并在失败时回滚到旧版本 如果你已经在终端里用 Pascal CLI 运行本地 P测试开发工具上一篇Promptify与智能搜索提升搜索引擎的相关性下一篇Ever® Traduora标签系统使用指南如何组织和分类翻译创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

相关新闻

腾讯云WorkBuddy Enterprise企业级AI Agent平台架构与实操指南

腾讯云WorkBuddy Enterprise企业级AI Agent平台架构与实操指南

1. 从零理解 WorkBuddy Enterprise 的定位与核心价值1.1 这个平台到底解决什么问题WorkBuddy Enterprise 是腾讯云推出的一套企业级 AI 平台与 Agent 生态产品。说白了,它要解决的核心问题是:企业想用 AI,但不知道怎么把 AI 能力安全、可控、…

2026/9/25 4:57:52 阅读更多 →
Endnote在Word中消失?COM加载项排查与修复指南

Endnote在Word中消失?COM加载项排查与修复指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/25 4:57:52 阅读更多 →
Java图书管理系统SWT实战:从环境搭建到避坑指南

Java图书管理系统SWT实战:从环境搭建到避坑指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/25 4:57:52 阅读更多 →

最新新闻

Skia PathKit:将 Skia PathOps 编译为 WASM 并在浏览器中使用的完整实战指南

Skia PathKit:将 Skia PathOps 编译为 WASM 并在浏览器中使用的完整实战指南

图形学图像处理 【免费下载链接】skia Skia is a complete 2D graphic library for drawing Text, Geometries, and Images. 项目地址: https://gitcode.com/gh_mirrors/skia1/skia 点击查看 免费下载 本文以 Skia 仓库中 modules/pathkit/README.md 的核心内容为骨…

2026/9/25 5:26:24 阅读更多 →
ng-zorro-antd 日期选择器进阶指南:用 nzRenderExtraFooter 在面板中渲染自定义页脚

ng-zorro-antd 日期选择器进阶指南:用 nzRenderExtraFooter 在面板中渲染自定义页脚

UI组件前端 【免费下载链接】ng-zorro-antd Angular UI Component Library based on Ant Design 项目地址: https://gitcode.com/gh_mirrors/ng/ng-zorro-antd 点击查看 免费下载 nzRenderExtraFooter 是 ng-zorro-antd 日期选择器组件族(nz-date-picke…

2026/9/25 5:26:24 阅读更多 →
Fission 统一 OpenTelemetry 可观测性落地:RFC-0019 指标迁移、OTLP 原生推送与依赖裁剪实战

Fission 统一 OpenTelemetry 可观测性落地:RFC-0019 指标迁移、OTLP 原生推送与依赖裁剪实战

云原生后端 【免费下载链接】fission Fast and Simple Serverless Functions for Kubernetes 项目地址: https://gitcode.com/gh_mirrors/fi/fission 点击查看 免费下载 导读 Fission 的 tracing 与 logging 早已原生使用 OpenTelemetry,唯独指标&…

2026/9/25 5:26:23 阅读更多 →
从实验室到整机落地:集成化生物信号采集与处理系统全记录

从实验室到整机落地:集成化生物信号采集与处理系统全记录

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/25 5:26:23 阅读更多 →
代码热更新原理与实践:从DevTools到Nacos配置中心

代码热更新原理与实践:从DevTools到Nacos配置中心

改代码最烦什么?不是需求复杂,不是报错看不懂,而是改一行前端样式要刷新整个页面重新走一遍流程,改一个Java方法要重启服务然后重新登录、重新点进那个页面。如果每天在这种“改代码-重启-验证”的循环里折腾几十次,你…

2026/9/25 5:26:23 阅读更多 →
使用 AWS SDK for Java 2.x 操作 Amazon Connect:实例、联系人、队列与历史指标实战指南

使用 AWS SDK for Java 2.x 操作 Amazon Connect:实例、联系人、队列与历史指标实战指南

示例工程教程后端 【免费下载链接】aws-doc-sdk-examples Welcome to the AWS Code Examples Repository. This repo contains code examples used in the AWS documentation, AWS SDK Developer Guides, and more. For more information, see the Readme.md file below. 项目地…

2026/9/25 5:25:23 阅读更多 →

日新闻

AI元人文:从工具使用到思维重构的深度探索

AI元人文:从工具使用到思维重构的深度探索

最近半年我一直在琢磨一件事:AI元人文到底是什么?说白了,就是“用元视角重新审视人与AI的关系”,也在“探索AI如何反向逼着我们发现自己的思考边界”。标题里的“元探索”,在我看就是一层套一层的追问——当你用AI解决…

2026/9/25 0:00:41 阅读更多 →
Python+CNN车牌识别实战:从数据预处理到模型训练与部署

Python+CNN车牌识别实战:从数据预处理到模型训练与部署

简介:基于Python与卷积神经网络的车牌识别项目,面向计算机视觉初学者及智能交通开发者,目标是帮助用户掌握从数据预处理、模型构建到实际部署的完整流程。压缩包共25个文件,包含jpg/png图像样本、py训练脚本、md说明文档、dat数据…

2026/9/25 0:00:41 阅读更多 →
Vim基础操作全攻略:保存退出、模式切换与高频命令实战

Vim基础操作全攻略:保存退出、模式切换与高频命令实战

1. 项目概述1.1 核心需求解析今天聊聊Vim。写这个题目的原因是:几乎每个后端开发者、运维人员、数据工程师某天都会遇到一个场景——深夜加班,服务器登录界面只有黑底白字,编辑器只有vi/vim,你必须在五分钟内完成一次配置修改并保…

2026/9/25 0:00:41 阅读更多 →

周新闻

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

直接铺开项目本身吧。这几个月我一直在折腾一件事:用Flutter给OpenHarmony做一款游戏集合类的App,说白了就是把若干小游戏塞进一个壳里,用统一入口分发。这个方向本身不算新鲜,真正让我花了不少心思的,是首页那堆游戏卡…

2026/9/24 14:34:13 阅读更多 →
Word表格编号全攻略:从列表编号到题注交叉引用

Word表格编号全攻略:从列表编号到题注交叉引用

写Word文档,最让人头疼的往往是那些“看起来不起眼”的小问题。比如表格编号这事:今天在表后面多加了两个空白行,明天给客户交稿前发现整个章节的编号全部错位,光是挨个改序号就能耗掉大半个下午。我前阵子帮人整理一份上百页的技…

2026/9/24 9:10:42 阅读更多 →
从第一个站到第二个站:独立开发者的静态网站选型与落地实践

从第一个站到第二个站:独立开发者的静态网站选型与落地实践

1. 项目概述1.1 核心需求解析做独立开发者这几年,说实话,第一个网站上线的那天晚上我兴奋得没睡着。但等它跑了半年,流量惨淡、功能臃肿、代码自己都懒得看第二遍之后,我才慢慢琢磨明白一个道理:第一个网站是练手&…

2026/9/24 14:33:56 阅读更多 →

月新闻

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能分类:[AI/大模型]细分主题:AI 增强型 CI/CD 流水线自动化与 GitOps 实践:Agent 工作流、工具调用与任务拆解:从原型到生产的验收清单很多团队在尝试用大…

2026/9/24 12:50:34 阅读更多 →
容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场分类:[工程技术]细分主题:Kubernetes 生产环境运维与排障实战:可复制的项目复盘模板与决策记录大部分团队的事故复盘报告,最后都变成了躺在 Confluence 或钉…

2026/9/24 14:33:48 阅读更多 →
容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步分类:[工程技术]细分主题:Docker 容器化技术与镜像安全管理:核心链路的逐步实现与关键代码取舍面对一个积累了五六年历史包袱的单体架构应用(包含 Web 接口、后台…

2026/9/24 12:49:17 阅读更多 →