Sinon sandbox.replace() 完全指南:安全替换对象属性并自动还原
测试开发工具【免费下载链接】sinonTest spies, stubs and mocks for JavaScript.项目地址https://gitcode.com/gh_mirrors/si/sinon点击查看免费下载Sinon 的sandbox.replace()用于在测试中临时替换对象上的任意属性方法、字符串、数值乃至 fake/spy/stub并将原始值登记在沙箱的还原队列中随sandbox.restore()一并自动恢复。它省去了手动保存与还原原值的样板代码是隔离被测对象依赖、注入测试替身的核心手段。读完本文你将掌握replace()的参数规则、同类型校验、异常边界、访问器属性的替代方案replaceGetter/replaceSetter及其底层还原机制。方法签名与基本语义sandbox.replace是沙箱 API 中的一个方法官方文档定义如下sandbox.replace(object, property, replacement);其语义为将object上的property属性替换为replacement参数并返回replacement。它属于 沙箱 API 方法列表 中的一员从sinon5.0.0起sinon根对象本身就是一个默认沙箱因此可以直接调用sinon.replace(...)无需手动创建沙箱。替换过程并非简单的属性赋值——沙箱会在背后记录一个还原器restorer当调用sandbox.restore()时会把属性还原为替换前的值从而保证测试之间互不污染。在 src/sinon/sandbox.js 中可以找到该方法的真实实现其执行流程包含四道校验见下文异常与边界一节随后通过push(fakeRestorers, getFakeRestorer(object, property))登记还原器再执行object[property] replacement完成替换。返回值返回 replacement 本身replace()会返回传入的replacement参数便于链式使用或立即持有引用。这一点在 test/src/sandbox-test.js 中有对应测试将replacement传入后断言返回值actual与replacement严格相等。一个实用的写法是让替换函数直接作为返回值被捕获const sandbox sinon.createSandbox(); const object { method() { return original; } }; const fakeMethod sandbox.replace(object, method, sinon.fake.returns(mocked)); fakeMethod(); // mocked object.method(); // mockedreplacement 可以是任意值这是replace()与replaceGetter/replaceSetter的一个关键差异replacement可以是任何类型的值不限于函数。官方文档明确说明它可以包含fakes、spies和stubs。具体来说函数替换函数最常见的场景例如用 stub 替换被测模块依赖的方法字符串/数值等原始值替换同类型值如替换配置对象的字段fake/spy/stub 替换方法替换后该属性立即拥有 Sinon 的断言与行为能力。test/src/sandbox-test.js 中的测试证明了非函数属性字符串同样可以被替换并在restore()后还原。同类型校验不允许跨类型替换虽然replacement可以是任意值但原属性与替换值必须是同一 JavaScript 类型。源码中的verifySameType校验src/sinon/sandbox.js会对比typeof original与typeof replacementif (originalType ! replacementType) { throw new TypeError(Cannot replace ${originalType} with ${replacementType}); }对应测试位于 test/src/sandbox-test.js用函数替换字符串属性 → 抛出Cannot replace string with function用字符串替换函数属性 → 抛出Cannot replace function with string。这一设计避免了替换后调用方行为不可预期的隐性故障例如把方法误替换成字符串导致运行期TypeError: not a function。异常与边界情况官方文档明确指出Attempting to replace an already replaced value causes an exception.尝试替换一个已被替换的值会抛出异常。结合源码replace()在赋值前会依次执行四道校验校验步骤触发条件异常信息源码位置checkForValidArguments属性不存在无属性描述符Cannot replace non-existent property X. Perhaps you meant sandbox.define()?src/sinon/sandbox.jscheckForValidArgumentsreplacement为undefinedExpected replacement argument to be defined同上verifyNotReplaced该属性已被同一沙箱替换过Attempted to replace X which is already replacedsrc/sinon/sandbox.jsthrowOnAccessors属性是 getterUse sandbox.replaceGetter for replacing getterssrc/sinon/sandbox.jsthrowOnAccessors属性是 setterUse sandbox.replaceSetter for replacing setters同上verifySameType原值与替换值类型不一致Cannot replace X with Ysrc/sinon/sandbox.js几个值得注意的细节重复替换限制无论是函数还是字符串同一对象同一属性在同一沙箱内只能替换一次第二次调用会抛TypeError。测试见 test/src/sandbox-test.js。非可写属性的陷阱如果属性由Object.defineProperty定义为writable: false但仍 configurablereplace()通过赋值方式写入时会抛原生错误Cannot assign to read only property ...且属性保持不变相关测试见 test/src/sandbox-test.js。访问器属性请使用 replaceGetter / replaceSetter官方文档强调replace()只作用于非访问器non-accessor属性。对于 getter/setter应当改用sandbox.replaceGetter(object, property, replacement)替换 getterreplacement必须是函数sandbox.replaceSetter(object, property, replacement)替换 setterreplacement必须是函数。若对访问器属性调用replace()会直接抛出Error而非TypeError提示改用对应方法见 test/src/sandbox-test.js。这两个方法同样支持替换继承来的访问器并在restore()后还原测试见 test/src/sandbox-test.js。如果你的确需要用赋值方式替换访问器属性例如对象同时定义了 getter 与 setter、且底层状态由外部变量承载replace()还有一个非文档化的变体sandbox.replace.usingAccessor(object, property, replacement)它跳过访问器检测、直接走赋值路径测试示例见 test/src/sandbox-test.js。日常测试优先使用replaceGetter/replaceSetter即可。与 restore() 的生命周期管理自动还原replace()的最大价值在于与sandbox.restore()配合实现自动清理。还原流程在 src/sinon/sandbox.js 中实现restore()会逆序执行fakeRestorers数组中的还原器再逆序对collection中的 fake 逐个调用restore。每个还原器由getFakeRestorer生成src/sinon/sandbox.js其还原策略是描述符敏感的属性原本是对象自有属性记录替换前的完整属性描述符还原时通过Object.defineProperty恢复——包括enumerable、writable等属性特征而不仅仅是值属性是继承自原型链的替换会在对象上创建自有属性shadowing还原时执行delete object[property]移除该自有属性使原型上的原值重新可见。test/src/sandbox-test.js 对这两种还原路径分别做了验证还原后自有属性恢复原描述符如enumerable: false保持原样继承属性则被取消遮蔽、恢复读取原型值。看一个官方文档测试中的完整生命周期示例来自 docs/tests/docs/sandboxes/api/restore.test.jsconst obj { one: 1, two: 2, three: 3 }; sinon.replace(obj, one, apple); sinon.replace(obj, two, banana); sinon.replace(obj, three, cherry); // obj 现在为 { one: apple, two: banana, three: cherry } sinon.restore(); // obj 恢复为 { one: 1, two: 2, three: 3 }与 sandbox.define() 的区别替换 vs 定义replace()要求属性必须已经存在否则抛出Cannot replace non-existent property ... Perhaps you meant sandbox.define()?。这个错误信息同时指向了沙箱的另一方法sandbox.define(object, property, value)实现见 src/sinon/sandbox.jsreplace替换已存在的属性要求同类型define在对象上新增一个原本不存在或来自原型但非自有的属性同样会被沙箱登记并在restore()时删除且不要求类型一致。两条路径的错误信息互为提示define对已存在属性也会提示Perhaps you meant sandbox.replace()?测试见 test/src/sandbox-test.js。选择原则很简单属性原本就有就用replace原本没有就用define。完整实战示例以下示例改编自官方文档测试 docs/tests/docs/sandboxes/api/replace.test.js演示了默认沙箱下的最小用法import * as sinon from sinon; // sinon 根对象即默认沙箱 const myObject { myMethod: function () { return apple pie; }, }; sinon.replace(myObject, myMethod, function () { return strawberry; }); const result myObject.myMethod(); // result strawberry方法已被替换 sinon.restore(); // myObject.myMethod 恢复为返回 apple pie 的原函数结合 stub 的行为能力做依赖替换const sandbox sinon.createSandbox({ useFakeTimers: true }); const service { fetchData: () Promise.resolve(real data) }; // 用 stub 替换方法并预置返回值 const stub sandbox.replace( service, fetchData, sandbox.stub().resolves(mocked data), ); await service.fetchData(); // mocked data stub.calledOnce; // true sandbox.restore(); // 同时清理 stub 与替换常见注意事项小结replace()每次调用前都会校验未被替换过同一属性的重复替换会抛异常需要先restore()或使用不同沙箱替换值必须与原始属性同类型否则抛TypeError访问器属性getter/setter请改用replaceGetter/replaceSetterrestore()是描述符敏感的自有属性按原描述符还原继承属性通过删除自有遮蔽属性还原若目标属性尚未定义应使用sandbox.define()而不是replace()默认沙箱sinon根对象已具备全部沙箱 API除非需要独立配置如单独的useFakeTimers、injectInto等否则无需手动createSandbox。赞分享测试开发工具【免费下载链接】sinonTest spies, stubs and mocks for JavaScript.项目地址https://gitcode.com/gh_mirrors/si/sinon点击查看免费下载相关推荐Sinon Sandbox 的 replaceSetter精确替换对象 Setter 的完整指南Sinon Sandbox 的 replaceSetter精确替换对象 Setter 的完整指南 导读 sandbox.replaceSetter objec测试开发工具CVAT 半自动与自动标注Serverless AI 工具安装与部署指南CVAT 半自动与自动标注Serverless AI 工具安装与部署指南 导读 本文面向希望在自托管 CVATComputer Vision Annota测试开发工具HivisionIDPhotosAI 证件照生成器CPU 30 秒出成品照HivisionIDPhotosAI 证件照生成器CPU 30 秒出成品照 入职前 1 小时发现交上去的蓝底证件照不合规格打印店又已打烊。Hivisio测试开发工具上一篇UpscalerJS 开源项目入门指南下一篇Sveltia CMS移动端体验响应式设计如何完美适配不同设备创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

相关新闻

华为FTTR全光家庭网:光纤入室解决WiFi6卡顿与多终端抢带宽

华为FTTR全光家庭网:光纤入室解决WiFi6卡顿与多终端抢带宽

简介:本资源为华为FTTR全光家庭网络创新解决方案的完整技术白皮书PDF,面向通信工程师、宽带网络规划人员、智慧家庭方案集成商及运营商装维技术人员,聚焦解决大户型Wi-Fi覆盖弱、千兆宽带实际速率不足、多终端卡顿掉线等家庭网络核心痛点。文…

2026/9/25 8:53:12 阅读更多 →
河北欧米奇西点西餐学校行业口碑如何

河北欧米奇西点西餐学校行业口碑如何

核心定位河北欧米奇西点西餐学校是中国东方教育集团旗下经石家庄市批准设立的正规西式餐饮职业学校,作为河北省西式餐饮专业人才培养重点基地,核心面向15-18岁青少年提供技能学历双提升的西式餐饮技能培训服务,深耕行业三十余年,已…

2026/9/25 8:53:12 阅读更多 →
制造经理的高效:从救火队长到系统管理员

制造经理的高效:从救火队长到系统管理员

做了十几年制造经理,我对“高效”这个词越来越警惕。面试的时候老板爱问“你怎么理解效率”,很多人脱口就是“结果导向、执行力强、按计划走”,听着都对,但放到现场基本没用。真正的高效不是你自己一天处理了多少件事,…

2026/9/25 8:52:12 阅读更多 →

最新新闻

从免费CRM到独立部署:小团队搭建私人CRM网站全记录

从免费CRM到独立部署:小团队搭建私人CRM网站全记录

上个月我终于把客户资料从微信聊天记录、Excel表格和记事本里统一搬了出来,全部塞进了一套自己部署的CRM系统里。项目代号DeskcommCRM,听起来像个大厂产品,其实是我基于开源组件和一台轻量云服务器搭起来的私人客户关系管理网站。到今天跑了1…

2026/9/25 12:52:24 阅读更多 →
逐行精读Tftpd64的tftpd_thread.c:TFTP状态机、OACK选项协商与重传策略完整实现

逐行精读Tftpd64的tftpd_thread.c:TFTP状态机、OACK选项协商与重传策略完整实现

逐行精读Tftpd64的tftpd_thread.c:TFTP状态机、OACK选项协商与重传策略完整实现 【免费下载链接】tftpd64 The working repository of the famous TFTP server. 项目地址: https://gitcode.com/gh_mirrors/tf/tftpd64 Tftpd64 是 Windows 平台上最著名的 TFT…

2026/9/25 12:52:24 阅读更多 →
Large Language Models for Summarizing Czech Historical Documents and Beyond

Large Language Models for Summarizing Czech Historical Documents and Beyond

文章主要内容与创新点总结 一、主要内容 本文聚焦捷克语文本摘要任务,尤其是历史文献摘要这一研究缺口,展开了系统性研究,具体内容如下: 研究背景:文本摘要旨在精简文本同时保留核心信息,当前该领域研究多集中于英语等资源丰富语言,而捷克语(尤其是历史捷克语)因语言…

2026/9/25 12:52:24 阅读更多 →
Windows 8.1原版镜像下载与校验:MSDN正式版、SHA1验证及UEFI/GPT安装指南

Windows 8.1原版镜像下载与校验:MSDN正式版、SHA1验证及UEFI/GPT安装指南

隔三差五就有人来问我:网上那些 Windows 8.1 纯净版、完美优化版、一键装机版,到底能不能用?我的回答一直没变——如果你需要的是一个稳定的 Windows 8.1 镜像下载,就老老实实找微软官方原版,尤其是带 MSDN 正式版字样…

2026/9/25 12:52:24 阅读更多 →
自建CRM系统全攻略:从LNMP架构到数据安全运维

自建CRM系统全攻略:从LNMP架构到数据安全运维

先说个背景。去年团队规模从三个人扩到十来个人的时候,我们做的第一件事不是换办公室,而是认真解决客户信息管理的问题。之前客户资料全躺在个人微信、Excel 表格和邮箱里,每个人记法还不一样,有人记在备注里,有人单独建了个文档&…

2026/9/25 12:52:24 阅读更多 →
开放式代码评审实践:让每一行代码都被认真读过

开放式代码评审实践:让每一行代码都被认真读过

1. 开放式代码评审:让每一行代码都被认真读过先聊个场景。你花了几个小时写了一个功能,提交了合并请求,两天后评审人才姗姗来迟,留下一句“LGTM”就合入了。你心里清楚,这份代码里有几处设计瑕疵,有些边界条…

2026/9/25 12:51: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/25 11:15:26 阅读更多 →
从第一个站到第二个站:独立开发者的静态网站选型与落地实践

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

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 阅读更多 →