首页/新闻资讯/正文详情

深入解析 Sinon 的 `assert.alwaysThrew`:验证 spy 是否每次都抛出异常

发布时间:2026/9/24 13:44:04 来源:云帆数科 栏目:资讯中心
深入解析 Sinon 的 `assert.alwaysThrew`:验证 spy 是否每次都抛出异常
测试开发工具【免费下载链接】sinonTest spies, stubs and mocks for JavaScript.项目地址https://gitcode.com/gh_mirrors/si/sinon点击查看免费下载Sinon 是 JavaScript 生态中最常用的测试替身test double库之一其内置的sinon.assert断言体系将fake、spy、stub的行为验证从手动判断布尔值升级为带详细错误信息的断言失败。本文聚焦其中专门用于异常行为的断言方法assert.alwaysThrew它校验被观测对象每一次调用是否都抛出了指定异常。读完本文你将掌握alwaysThrew的完整签名、参数语义、通过/失败条件、与assert.threw的差异以及它在 Sinon 源码中的底层实现原理能够直接将其应用到自己的测试代码中。一、assert.alwaysThrew(spy, exception)的语义alwaysThrew是sinon.assert断言家族的一员其行为与alwaysCalledWith等 always 系列一致不关心某一次调用是否满足条件而是要求全部调用都满足。签名sinon.assert.alwaysThrew(spy[, exception]);通过条件当fake、spy或stub的每一次调用都抛出给定的异常时断言通过。exception参数的两种合法形式形式说明匹配逻辑String异常类型名例如TypeError、RangeError与抛出的异常对象的name属性比对实际对象异常实例例如new TypeError(not an apple pie)与抛出的异常对象做严格引用比较省略exception参数当只传入一个参数时断言退化为spy 是否每次都抛出了任意异常——只要所有调用都抛出异常即通过不关心具体类型或实例。从实现角度exception的匹配逻辑定义在 src/sinon/proxy-call.js 的threw方法中threw: function threw(error) { if (typeof error undefined || !this.exception) { return Boolean(this.exception); } return this.exception error || this.exception.name error; },可以看到未传error时仅判断该次调用是否抛出了异常传入后则分别支持引用相等对象与name属性相等字符串类型名两种比对方式。注意它是先比较引用this.exception error再比较namethis.exception.name error因此即使传入一个与抛出异常同类型的字符串名也能正确匹配。二、通过条件与失败条件alwaysThrew的核心判定可以概括为三条规则全部调用都抛出指定异常→ 通过不产生任何错误存在任意一次调用没有抛出指定异常或没有抛出异常→ 失败一次都没有被调用过→ 视为不满足总是抛出的前提断言同样失败。失败时抛出的错误是ErrorAssertError消息格式定义在 src/sinon/assert.jsmirrorPropAsAssertion(alwaysThrew, %n did not always throw exception%C);即默认错误消息为对象名 did not always throw exception其中%n是 spy/fake/stub 的函数名匿名时用anonymous%C是错误发生的调用栈。相比直接调用spy.alwaysThrew()得到true/false断言失败会携带函数名与调用上下文便于快速定位。三、完整示例两种异常匹配方式原文档给出了可直接运行的完整示例这里完整保留并补充失败分支的解读import * as sinon from sinon; // 场景一fake 从未抛出异常 const f1 sinon.fake(); f1(apple pie); sinon.assert.alwaysThrew(f1, TypeError); // Uncaught Error [AssertError]: fake did not always throw exception // 原因f1 唯一的一次调用没有抛异常all calls threw 不成立 // 场景二fake 每次都抛出指定类型的异常 const f2 sinon.fake.throws(new TypeError(not an apple pie)); try { f2(apple pie); } catch (err) { // 业务代码中通常在此处理异常测试中需显式捕获以继续后续断言 } // Generates no error —— 断言通过 sinon.assert.alwaysThrew(f2, TypeError);关键点解读sinon.fake.throws(new TypeError(not an apple pie))创建了一个每次调用都会抛出该异常实例的 fake参见 fakes 文档 中的throws行为由于f2抛出的是TypeError实例TypeError字符串会通过this.exception.name error分支命中调用f2(apple pie)时必须用try/catch包裹否则异常会直接穿透测试若希望按对象引用匹配可传入相同的异常实例sinon.assert.alwaysThrew(f2, new TypeError(...))仅在传入实例与抛出实例严格相等时通过。四、在测试框架中使用基于仓库测试用例仓库在 docs/tests/docs/assertions/api/always-threw.test.js 中提供了使用tap测试框架的完整验证用例覆盖了上述两条核心路径import tap from tap; import * as sinon from sinon; // 用例一所有调用都抛出异常时断言通过 tap.test(assert.alwaysThrew - passes when all calls threw, (t) { const fake sinon.fake.throws(new Error(boom)); try { fake(); } catch (e) {} try { fake(); } catch (e) {} t.doesNotThrow(() { sinon.assert.alwaysThrew(fake); }, assertion should pass when all calls threw); t.end(); }); // 用例二某次调用没有抛出异常时断言失败 tap.test(assert.alwaysThrew - fails when one call didnt throw, (t) { const fake sinon.fake(); fake(); t.throws( () sinon.assert.alwaysThrew(fake), /fake did not always throw exception/, assertion should fail when not all calls threw ); t.end(); });这两条用例可以视为alwaysThrew的行为契约正向用例fake连续两次抛出异常无论是否传exception断言不抛错反向用例fake存在一次未抛出异常的调用断言抛出错误且错误消息匹配/fake did not always throw exception/。在 Jest、Mocha、Vitest 等框架中用法完全一致——sinon.assert的失败形式统一为抛出Error可以自然地被各框架的expect(() ...).toThrow()、assert.throws等机制捕获。五、alwaysThrew与threw的区别容易混淆的一对 API 是assert.threw与assert.alwaysThrew断言判定粒度通过条件失败消息assert.threw(spy, exception)至少一次存在至少一次调用抛出指定异常%n did not throw exceptionassert.alwaysThrew(spy, exception)全部调用每一次调用都抛出指定异常%n did not always throw exception从 src/sinon/proxy.js 的注册代码可以清晰看到两者在代理层的关联delegateToCalls(proxyApi, threw, true); delegateToCalls(proxyApi, alwaysThrew, false, threw);alwaysThrew被注册为delegateToCalls的代理方法matchAny false并把实际判定委托给每个调用的threw方法——也就是说alwaysThrew就是对每一次调用的threw结果取逻辑与。同样地returned/alwaysReturned、calledWithNew/alwaysCalledWithNew也是成对出现的模式完全一致。六、源码级原理delegateToCalls如何实现总是语义alwaysThrew的底层实现位于 src/sinon/proxy-call-util.js 的delegateToCalls函数。其核心逻辑可以概括为未调用保护如果 proxy 从未被调用!this.called且没有传入notCalled兜底函数直接返回false——对应零次调用不满足 always 断言的语义遍历全部调用从第 0 次到第callCount - 1次逐次取出this.getCall(i)调用其actual指定的方法此处为threw并收集结果matchAny开关matchAny true如threw只要遇到一次匹配就提前return truematchAny false如alwaysThrew必须全部调用都返回true最终return matches this.callCount才通过。这段代码揭示了alwaysThrew与threw本质上是同一个delegateToCalls机制下全部匹配与任一匹配两个分支行为可预期、可推导。而断言层的错误消息则由mirrorPropAsAssertion(alwaysThrew, %n did not always throw exception%C)提供src/sinon/assert.js将false结果转换为携带对象名与调用栈的AssertError。七、适用场景与使用建议验证错误路径的完备性当被测函数在多种非法输入下都应抛出同一类异常例如参数校验抛TypeError时用alwaysThrew可以一次性断言所有路径都抛了比逐个断言更简洁与fake.throws搭配做负面契约测试sinon.fake.throws(...)创建的 fake 天然满足总是抛出适合作为异常来源的替身注入被测模块参考 fakes 文档 与 stubs 文档注意异常被吞的场景如果被观测对象内部用try/catch吞掉了异常alwaysThrew会因为没有调用抛出异常而失败——这恰好是有价值的回归信号优先使用sinon.assert而非裸方法直接调用fake.alwaysThrew(...)只返回布尔值失败时缺少上下文而断言版会给出函数名与调用栈配合sinon.assert.expose或自定义sinon.assert.fail可以无缝接入团队既有的断言框架。完整的断言 API 清单与其余兄弟方法threw、called、calledWith等可查阅 Assertions API 文档其失败消息与实现模式均与alwaysThrew保持一致。赞分享测试开发工具【免费下载链接】sinonTest spies, stubs and mocks for JavaScript.项目地址https://gitcode.com/gh_mirrors/si/sinon点击查看免费下载相关推荐SingleFile完整指南如何一键保存完整网页到单个HTML文件SingleFile完整指南如何一键保存完整网页到单个HTML文件 在信息时代我们每天都会遇到需要保存的宝贵网页内容——研究资料、技术文档、新闻文章或是值测试开发工具gh_mirrors/as/assert函数断言测试PHP函数是否抛出预期异常gh_mirrors/as/assert函数断言测试PHP函数是否抛出预期异常 在PHP开发中验证函数是否按预期抛出异常是保证代码健壮性的重要环节。本文将详后端Beekeeper Studio 开源 UI Kit 之 Data Editor集成表格、实体树与 SQL 编辑器的数据编辑组件开发指南Beekeeper Studio 开源 UI Kit 之 Data Editor集成表格、实体树与 SQL 编辑器的数据编辑组件开发指南 导读 Data Ed测试开发工具上一篇DotNetJS 使用教程下一篇ItsycalMac菜单栏上的终极轻量级日历工具创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

相关推荐

《Python程序设计基础》Git 操作指南
《Python程序设计基础》Git 操作指南

1. 引言 在《Python程序设计基础》课程中,除了编写 Python 代码,掌握版本控制工具 Git 同样重要。Git 能帮助你管理代码的历史版本、多人协作开发,以及安全地保存每一次修改。本教程将带你从零开始,掌握 Git 的常用操作&#xff… · 2026/9/24 13:44:04

EasyWeChat 5.x 微信公众号永久素材管理指南:上传、图文、列表与下载实战
EasyWeChat 5.x 微信公众号永久素材管理指南:上传、图文、列表与下载实战

后端即时通讯 【免费下载链接】easywechat 📦 一个 PHP 微信 SDK 项目地址: https://gitcode.com/gh_mirrors/ea/easywechat 点击查看 免费下载 微信公众号中的图片、语音、视频等多媒体资源,并不能直接拿来就用于消息发送,而是需… · 2026/9/24 13:43:46

Feign SOAP 编解码实战:用 feign-soap-jakarta 模块实现 SOAP 1.1/1.2 客户端与 SOAPFault 处理
Feign SOAP 编解码实战:用 feign-soap-jakarta 模块实现 SOAP 1.1/1.2 客户端与 SOAPFault 处理

后端API设计 【免费下载链接】feign Feign makes writing java http clients easier 项目地址: https://gitcode.com/gh_mirrors/fe/feign 点击查看 免费下载 本指南以 OpenFeign 仓库中的 soap-jakarta 模块为核心,讲解如何借助 JAXB 与 SOAPMessage 在… · 2026/9/24 13:43:40

Flutter鸿蒙适配:screen_protector防截屏插件开发实战
Flutter鸿蒙适配:screen_protector防截屏插件开发实战

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

深入解析 OpenKruise:Kubernetes 增强工作负载与原地升级实战指南
深入解析 OpenKruise:Kubernetes 增强工作负载与原地升级实战指南

深入解析 OpenKruise:Kubernetes 增强工作负载与原地升级实战指南 【免费下载链接】kubernetes-handbook Kubernetes 架构与生态:从云原生到 AI 原生基础设施的构建指南 项目地址: https://gitcode.com/gh_mirrors/ku/kubernetes-handbook OpenKr… · 2026/9/24 14:24:38

Phoenix LDAP 认证设计决策的可逆性分析:One-Way Door 与 Two-Way Door 框架的工程实践
Phoenix LDAP 认证设计决策的可逆性分析:One-Way Door 与 Two-Way Door 框架的工程实践

可观测性AI 评测LLMOpsAI 应用人工智能 【免费下载链接】phoenix AI Observability & Evaluation 项目地址: https://gitcode.com/gh_mirrors/phoenix13/phoenix 点击查看 免费下载 本篇技术指南深入解析 Phoenix(AI Observability & Evaluatio… · 2026/9/24 14:24:32

StoryDiffusion:长序列故事图像生成工具,让多帧漫画中的角色保持一致
StoryDiffusion:长序列故事图像生成工具,让多帧漫画中的角色保持一致

StoryDiffusion:长序列故事图像生成工具,让多帧漫画中的角色保持一致 【免费下载链接】StoryDiffusion Accepted as [NeurIPS 2024] Spotlight Presentation Paper 项目地址: https://gitcode.com/GitHub_Trending/st/StoryDiffusion StoryDiffus… · 2026/9/24 14:24:32

IronClaw 能力调用膜(CapabilityHost):特权效果的单一授权入口架构解析
IronClaw 能力调用膜(CapabilityHost):特权效果的单一授权入口架构解析

人工智能AI 应用交互助手AI Agent 【免费下载链接】ironclaw IronClaw is an Agent OS focused on privacy, security and extensibility 项目地址: https://gitcode.com/gh_mirrors/iro/ironclaw 点击查看 免费下载 导读 本文以 crates/kernel/ironclaw_capabili… · 2026/9/24 14:24:32

ESP32 SPI驱动W5500以太网实战:从原理到代码
ESP32 SPI驱动W5500以太网实战:从原理到代码

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

基于YOLOv8的渔船作业监控系统:从环境搭建到边缘部署全流程
基于YOLOv8的渔船作业监控系统:从环境搭建到边缘部署全流程

简介:这是一套面向计算机、人工智能、自动化等专业学生与教师的毕业设计级项目资源,围绕YOLOv8实现渔船作业监控系统,可用于毕设、课程设计、大作业或项目立项演示。压缩包共97个文件,约24.21MB,以70个Python源码文件为… · 2026/9/24 0:00:13

1D-CNN时间序列建模实战:从Conv1d原理到工业落地
1D-CNN时间序列建模实战:从Conv1d原理到工业落地

简介:面向时间序列数据建模的一维卷积神经网络完整实现,适合深度学习入门者及需要快速验证时序模型的研究者,能够从音频、文本、传感器或股价等序列中挖掘局部特征与时间依赖。压缩包体积很小,只有3KB,内含3个Python脚… · 2026/9/24 0:00:26

柔软的L:汉语语流中被忽视的舌肌张力控制
柔软的L:汉语语流中被忽视的舌肌张力控制

1. 这个“L”不是字母表里的L,而是舌尖上的L最近在几个方言群和语音教学社群里,反复看到有人发一句:“也说字母L:柔软的长舌”。初看以为是英语发音课笔记,点开才发现全是方言爱好者、播音系学生、语言康复师甚至戏曲演… · 2026/9/24 0:00:44

了解更多?预约专属演示

我们的顾问将为您一对一讲解产品与方案

企业微信二维码