
测试开发工具【免费下载链接】sinonTest spies, stubs and mocks for JavaScript.项目地址https://gitcode.com/gh_mirrors/si/sinon点击查看免费下载导读stub.onCall(index)是 Sinon stub 中专门用于按调用次序第 N 次调用定制行为的 API它让第一次调用返回 A、第二次调用返回 B、第三次抛异常这类顺序交互场景变得直接可写。本文以官方文档 docs/concepts/stubs/api/on-call.md 为主体结合源码实现src/sinon/stub.js、src/sinon/behavior.js与仓库测试test/src/stub-test.js、docs/tests/docs/stubs/api/on-call-1.test.js进行纵深解读你将掌握onCall的索引语义与回退机制、三个便捷别名的用法、与withArgs组合的正确姿势及一个必须避开的反模式以及它在重试、限流、分页等真实业务场景中的落地写法。onCall(index)是什么按调用次序精准定制stub.onCall(index)用于定义 stub 在第n次调用时的行为索引index从0开始计数即onCall(0)对应第一次调用。它非常适合测试顺序交互例如轮询、重试逻辑、逐页拉取数据等场景中同一函数在不同调用轮次需要返回不同结果。官方文档给出的定位是Defines the behavior of the stub on thenthcall. Useful for testing sequential interactions.从源码看其实现非常直接src/sinon/stub.jsonCall: function onCall(index) { if (!this.behaviors[index]) { this.behaviors[index] behavior.create(this); } return this.behaviors[index]; },每次调用onCall(index)时stub 会按索引把行为对象缓存在内部的behaviors数组中不存在则新建并返回该行为对象——这正是它可以继续链式调用returns、throws、callsFake等行为方法的原因。注意仅调用onCall(index)本身不会产生任何行为副作用它只是取到或创建了一个行为容器真正生效的是随后挂到它上面的行为定义这一点在 test/src/stub-test.js 中有专门用例验证does not create undefined behaviour just by calling onCall。基础用法按调用顺序返回不同值仓库文档配套测试 docs/tests/docs/stubs/api/on-call-1.test.js 给出了最经典的顺序返回值示例import tap from tap; import * as sinon from sinon; const callback sinon.stub(); callback.onCall(0).returns(Apple pie); callback.onCall(1).returns(Blueberry pie); callback.returns(Raspberry pie); // 默认行为回退值 callback(); // Apple pie 第一次调用 callback(); // Blueberry pie 第二次调用 callback(); // Raspberry pie 第三次调用回退到默认 callback(); // Raspberry pie 后续所有调用继续回退到默认关键点有三索引从 0 开始onCall(0)管第一次调用onCall(1)管第二次调用依此类推可以跳跃式指定无需连续铺满每个索引只声明关心的那几次即可回退机制一旦声明的指定行为用尽第 3 次及以后stub 自动回退到默认行为这里是由callback.returns(Raspberry pie)定义的默认值。回退机制在源码中的实现是 src/sinon/stub.js 的getCurrentBehaviorfunction getCurrentBehavior(stubInstance) { const currentBehavior stubInstance.behaviors[stubInstance.callCount - 1]; return currentBehavior currentBehavior.isPresent() ? currentBehavior : getDefaultBehavior(stubInstance); }每次调用时Sinon 先根据callCount从behaviors数组取出对应索引的行为只有当该行为确实存在且已被定义过isPresent()时才会使用它否则一律回退到defaultBehavior即returns(...)、throws(...)等设置的默认行为见 src/sinon/stub.js 的getDefaultBehavior。这正是指定行为用尽后回退默认的底层原理。便捷别名onFirstCall/onSecondCall/onThirdCall为了让 stub 定义更易读Sinon 提供了三个纯语法糖别名它们与onCall(index)完全等价见 src/sinon/stub.js别名等价写法语义stub.onFirstCall()stub.onCall(0)第一次调用的行为stub.onSecondCall()stub.onCall(1)第二次调用的行为stub.onThirdCall()stub.onCall(2)第三次调用的行为对应文档分别为 docs/concepts/stubs/api/on-first-call.md、docs/concepts/stubs/api/on-second-call.md、docs/concepts/stubs/api/on-third-call.md。仓库文档配套测试 docs/tests/docs/stubs/api/on-first-call.test.js 演示了onFirstCall的用法const callback sinon.stub(); callback.onFirstCall().returns(Apple pie); callback.returns(Raspberry pie); callback(); // Apple pie 第一次调用 callback(); // Raspberry pie 第二次调用回退默认 callback(); // Raspberry pie 后续所有调用回退默认别名可以混合、可以链式三个别名不仅彼此可混用还能与onCall直接链式拼接。仓库核心测试 test/src/stub-test.js 展示了别名 默认 别名的混合序列const stub sinon.stub().returns(3); // 默认返回 3 stub.onFirstCall().returns(1).onCall(2).returns(2); // 第 1 次返回 1第 3 次返回 2 stub(); // 1 第 1 次命中 onFirstCall stub(); // 3 第 2 次无指定行为回退默认 stub(); // 2 第 3 次命中 onCall(2) stub(); // 3 第 4 次指定行为用尽回退默认链式声明一整段序列onCall返回行为对象而行为对象上的方法如returns会返回 stub 自身因此可以把整段顺序行为串成一条链。官方测试同样覆盖了这种写法test/src/stub-test.jsconst stub sinon.stub() .onCall(0).returns(1) .onCall(1).returns(2) .onCall(2).returns(3); stub(); // 1 stub(); // 2 stub(); // 3与withArgs组合按参数 按次序双重定制onCall可以与本 API 中所有行为定义方法组合使用其中最有价值的是与withArgs的组合——它允许你针对特定参数的调用再按调用次序分别定制行为。官方文档配套测试 docs/tests/docs/stubs/api/on-call-2.test.js 给出了完整示例const callback sinon.stub(); const FORTY_TWO 42; const UNKNOWN_VALUE any unknown value; callback .withArgs(FORTY_TWO) .onFirstCall() .returns(Apple pie) .onSecondCall() .returns(Blueberry pie); callback.returns(Raspberry pie); // 默认行为 callback(UNKNOWN_VALUE); // Raspberry pie 未知参数走默认 callback(FORTY_TWO); // Apple pie 第 1 次传 42 callback(UNKNOWN_VALUE); // Raspberry pie 未知参数走默认 callback(FORTY_TWO); // Blueberry pie 第 2 次传 42 callback(FORTY_TWO); // Raspberry pie 第 3 次传 42指定行为用尽回退默认这个例子的精妙之处在于**回退是分层级的**为参数42定义的指定行为第 1、2 次用尽后callback(FORTY_TWO)回退到的并非全局默认行为Raspberry pie因为withArgs创建的 fake 没有自己的默认行为而是先尝试 fake 自身的默认行为没有则最终落到 stub 的默认行为。官方文档特意提醒读者注意Note how the behavior of the stub for argumentFORTY_TWOfalls back to the default behavior once no more calls have been defined.仓库核心测试 test/src/stub-test.js 还演示了withArgsfake 自带默认行为 指定调用行为的完整序列const stub sinon.stub().returns(0); // stub 默认返回 0 stub.withArgs(5) .returns(-1) // fake 默认返回 -1 .onFirstCall().returns(1) // 第 1 次传 5返回 1 .onSecondCall().returns(2); // 第 2 次传 5返回 2 stub(0); // 0 不匹配参数走 stub 默认 stub(5); // 1 第 1 次传 5 stub(0); // 0 不匹配参数走 stub 默认 stub(5); // 2 第 2 次传 5 stub(5); // -1 第 3 次传 5指定行为用尽回退到 fake 自己的默认组合顺序必须先withArgs再onCall反之会抛错顺序是有讲究的正确写法是stub.withArgs(...).onCall(...)而stub.onCall(...).withArgs(...)是不被支持的反模式。后者会在运行时直接抛出错误错误信息明确指出正确用法见 src/sinon/behavior.jsDefining a stub by invoking stub.onCall(...).withArgs(...) is not supported. Use stub.withArgs(...).onCall(...) instead.核心测试 test/src/behavior-extra-test.js 专门锁定这一行为确保后续版本不会悄悄放行这种写法。与更多行为方法组合returnsArg、returnsThis、throws等onCall的第 N 次定制并不局限于返回值它可以与 stub API 中的全部行为定义方法组合使用。仓库核心测试从多个维度验证了这一点test/src/stub-test.js与returnsArg组合——第 2 次调用返回传入的参数本身const stub sinon.stub().returns(default); stub.onSecondCall().returnsArg(0); stub(1); // default 第 1 次默认 stub(2); // 2 第 2 次返回参数 2 stub(3); // default 第 3 次回退默认与returnsThis组合——第 2 次调用返回this上下文const instance {}; instance.stub sinon.stub().returns(default); instance.stub.onSecondCall().returnsThis(); instance.stub(); // default 第 1 次默认 instance.stub(); // instance 第 2 次返回 this instance.stub(); // default 第 3 次回退默认与throws组合——第 2 次调用抛出指定异常对测试第一次成功、第二次开始失败的重试逻辑尤其有用const stub sinon.stub(); const error new Error(); stub.onSecondCall().throwsException(error); stub(); // 第 1 次正常返回 stub(); // 第 2 次抛出 error其他可组合的方法还包括callsFake第 N 次调用自定义实现、resolves/rejects第 N 次调用返回 Promise 结果等完整清单可参考 docs/concepts/stubs/api/index.md 中的 Methods 列表。注意onCall返回的是行为对象若需要对其调用withArgs请始终回到先withArgs后onCall的顺序。底层原理行为数组、默认行为与回退判定综合 src/sinon/stub.js 的源码onCall的完整运行机制可以总结为三层存储层每个 stub 实例持有behaviors数组索引即调用序次与defaultBehavior默认行为。onCall(index)只是按索引惰性创建并返回行为对象src/sinon/stub.js取值层每次实际调用时getCurrentBehavior用callCount - 1作为索引取行为只有行为存在且isPresent()才命中否则交给getDefaultBehavior回退src/sinon/stub.js回退层getDefaultBehavior的优先级为「stub 自身defaultBehavior→ 父级行为 → 新建空行为」。在withArgs场景下withArgs创建的是一个独立 fake见 src/sinon/stub.js它自带一份defaultBehavior可通过withArgs(...).returns(...)设置所以指定行为用尽时先回退到 fake 默认、最终才落到 stub 默认——这正是 on-call-2.test.js 中第 3 次callback(FORTY_TWO)返回Raspberry piestub 默认的完整链路。另外需要留意resetBehavior()会把defaultBehavior置空并清空behaviors数组src/sinon/stub.js因此onCall定义的顺序行为在resetBehavior()或reset()之后会全部失效若需要保留行为定义而只清空调用记录应使用resetHistory()。详见 docs/concepts/stubs/api/reset-behavior.md 与 docs/concepts/stubs/api/reset-history.md。实战场景顺序交互测试三板斧基于上述能力onCall族 API 最典型的应用场景包括场景一模拟首次失败、重试后成功——用onCall模拟不稳定的下游服务const fetchData sinon.stub(); fetchData.onCall(0).rejects(new Error(network timeout)); // 第 1 次失败 fetchData.onCall(1).resolves({ id: 1 }); // 第 2 次成功 fetchData.resolves({ id: 1 }); // 之后保持成功 // 被测代码带重试逻辑的数据拉取 await fetchData(); // 抛出 network timeout触发重试 await fetchData(); // 返回 { id: 1 } await fetchData(); // 返回 { id: 1 }场景二模拟分页游标——第 1 次返回一页数据、第 2 次返回空页以终止循环const listItems sinon.stub(); listItems.onCall(0).returns([1, 2, 3]); listItems.returns([]); // 后续返回空数组终止分页循环场景三验证调用顺序与参数演进——配合withArgs断言同一次交互中参数随时间变化const logger sinon.stub(); logger.withArgs(info).onFirstCall().returns(level-up);相关文档与源码索引官方 API 文档docs/concepts/stubs/api/on-call.md、docs/concepts/stubs/api/on-first-call.md、docs/concepts/stubs/api/on-second-call.md、docs/concepts/stubs/api/on-third-call.mdStub 全量 API 目录docs/concepts/stubs/api/index.md文档配套测试用例docs/tests/docs/stubs/api/on-call-1.test.js、docs/tests/docs/stubs/api/on-call-2.test.js、docs/tests/docs/stubs/api/on-first-call.test.js核心源码实现src/sinon/stub.jsonCall/onFirstCall/onSecondCall/onThirdCall、getCurrentBehavior、getDefaultBehavior、src/sinon/behavior.js行为对象转发及onCall(...).withArgs(...)报错逻辑核心测试验证test/src/stub-test.js顺序序列、链式声明、withArgs组合与回退、test/src/behavior-extra-test.js反模式报错小结stub.onCall(index)以 0 起始的索引精准刻画第 N 次调用的行为配合onFirstCall/onSecondCall/onThirdCall三个别名提升可读性与withArgs组合后能同时按参数 次序两个维度定制行为所有指定行为用尽后自动回退到默认行为形成一套完整的顺序交互测试方案。使用时牢记两条规则先withArgs再onCall反向会抛错onCall本身不产生行为行为必须通过returns/throws/callsFake等方法定义。赞分享测试开发工具【免费下载链接】sinonTest spies, stubs and mocks for JavaScript.项目地址https://gitcode.com/gh_mirrors/si/sinon点击查看免费下载相关推荐5步构建专业级音乐体验洛雪音乐开源音源完整实施指南5步构建专业级音乐体验洛雪音乐开源音源完整实施指南 洛雪音乐开源音源项目为音乐爱好者提供了一个完整的音源解决方案通过科学的音质分级体系和多平台兼容设计帮助音视频GoogleTest Mock Actions 详解掌握测试替身行为控制GoogleTest Mock Actions 详解掌握测试替身行为控制 什么是 Mock Actions 在单元测试中Mock Actions模拟行为测试质量保障开发工具Bootstrap Table 事件系统详解自定义交互行为的实现方法Bootstrap Table 事件系统详解自定义交互行为的实现方法 事件系统基础架构 Bootstrap Table 事件系统Event System是前端UI组件上一篇3分钟完成Windows系统激活的智能解决方案KMS_VL_ALL_AIO完全指南下一篇yuzu模拟器在PC上体验Switch游戏的完整指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考