ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

排查清单:sinon-chai 断言失败的 8 个高频坑与解决方案

排查清单:sinon-chai 断言失败的 8 个高频坑与解决方案 排查清单sinon-chai 断言失败的 8 个高频坑与解决方案【免费下载链接】sinon-chaiExtends Chai with assertions for the Sinon.JS mocking framework.项目地址: https://gitcode.com/gh_mirrors/si/sinon-chaisinon-chai 是一个把 Sinon.JS 的 spy、stub、mock 断言能力无缝接入 Chai 的测试插件让你能用expect(spy).to.have.been.calledWith(...)这类自然语法编写单元测试。可一旦 sinon-chai 断言失败新手常被TypeError、AssertionError和各种报错信息绕晕。这份排查清单总结了 8 个高频坑每个都附上症状、根因与解决方案帮你从盲猜变成精准定位。坑位典型报错一句话原因1calledWith is not a function插件没注册2is not a spy or a call to a spy!断言对象不是 spy3Cannot read properties of undefinedshould 接口未启用4... but it was never called异步断言过早5alwaysCalledWith不生效always 位置写错6参数不匹配calledWith 与 calledWithExactly 混用7callCount 莫名偏大未重置 spy用例污染8断言行为集体异常Chai / Sinon 版本不兼容坑 1忘记注册插件chai.use(sinonChai)—— 最常见的断言失败原因症状TypeError: spy.have is not a function或TypeError: spy.calledWith is not a function。根因sinon-chai 通过 Chai 插件机制向chai.Assertion.prototype注入called、calledWith、returned、thrown等断言核心注册逻辑都在lib/sinon-chai.js中。不执行chai.use(sinonChai)这些方法根本不存在自然一切断言都失败。解决在测试入口或公共 fixture统一注册import * as chai from chai; import sinonChai from sinon-chai; chai.use(sinonChai);坑 2对非 spy 对象断言 —— is not a spy or a call to a spy!症状TypeError: function foo(){} is not a spy or a call to a spy!根因sinon-chai 只接受三类对象sinon.spy()、sinon.stub()、sinon.mock()创建的探针以及它们的 call 对象如spy.getCall(0)、spy.lastCall。源码中的assertCanWorkWith会检查对象是否具备getCall与calledWithExactly方法见lib/sinon-chai.js普通函数、普通对象一概拒绝。解决确认断言目标确实是被包装后的 spy。最典型的翻车点是函数内部调用了原函数却拿原函数引用去断言。坑 3should 接口没启用 —— 少了一行 chai.should()症状明明注册了插件spy.should.have.been.called仍报TypeError: Cannot read properties of undefined (reading have)。根因.should语法依赖 Chai 为对象挂载.should属性这需要显式调用chai.should()才会生效与chai.use(sinonChai)是两件事。解决chai.should(); chai.use(sinonChai);建议把这两行都放进test/common.js这类公共文件中所有用例自动生效。坑 4异步回调还没执行就断言 —— but it was never called症状AssertionError: expected spy to have been called at least once, but it was never called而代码里明明调用了目标函数。根因异步场景下断言先于回调执行例如setTimeout、Promise、事件回调尚未触发spy 的调用记录还是空的。解决回调风格在回调内部断言或用 mocha 的done等待现代风格await异步操作完成后再断言时间相关逻辑用 sinon 的 fake timers 手动推进时间。坑 5always 关键字放错位置 —— 固定语序要记牢症状spy.should.have.been.alwaysCalledWith(...)不生效或行为诡异。根因这是官方文档明确标注的坑。always是 Chai 侧的修饰前缀必须放在have之前。正确写法是spy.should.always.have.been.calledWith(...)sinon-chai 内部正是读取always标志位来选择alwaysCalledWith等方法见lib/sinon-chai.js的createSinonMethodHandler。解决记死语序always → have → been → 断言动词例如spy.should.always.have.returned(42)。坑 6calledWith 与 calledWithExactly 语义混用这是参数类断言失败的重灾区两者约束完全不同断言匹配规则适用场景calledWith(...)子集匹配允许 spy 携带额外参数至少以这些参数调用过calledWithExactly(...)参数个数与内容必须完全一致严格校验每次调用calledOnceWithExactly(...)仅调用一次且参数完全一致最严格的组合另外注意calledWith对对象参数做深比较基于 samsam 的 deepEqual但函数等引用类型仍需配合calledWithMatchsinon.match才能模糊匹配。相关断言行为可参考test/callArguments.js中的测试用例。坑 7忘记重置 spy —— 测试间相互污染症状单个用例单独跑通过整组跑就挂callCount总是比预期多几次。根因同一文件里多个it共享 spy 状态上一次用例的调用记录残留到下一次。解决在afterEach中统一清理afterEach(() { sinon.restore(); // 恢复所有被替换的 spy / stub // 或 spy.resetHistory(); // 只清空调用记录 });坑 8版本不兼容与 calledBefore / calledAfter 陷阱症状安装后断言行为集体异常或calledBefore/calledAfter意外返回 false。根因与解决版本兼容sinon-chai 的peerDependencies要求 chai^5.0.0 || ^6.0.0、sinon 4.0.0见package.json新项目建议直接上 chai 5 与最新版 sinon避免新旧 API 差异导致断言失败时序断言当两个 spy 都没有任何调用记录时calledBefore/calledAfter无法判定先后会返回 false。先各自调用一次再断言顺序即可。附30 秒快速排查顺序 是否在所有测试文件之前执行了chai.use(sinonChai)断言对象是不是 spy / stub / mock 的返回值should 写法前是否执行了chai.should()异步回调是否已经完成语序对不对spy.should.always.have.been.calledWith(...)用的是calledWith还是calledWithExactly语义是否符合预期afterEach里是否重置了 spychai 与 sinon 的版本是否满足要求小结sinon-chai 断言失败绝大多数不是库的问题而是注册、接口、时序、语义和清理这五类细节。把这 8 个坑的排查顺序背下来配合lib/sinon-chai.js的断言注册逻辑和test/目录下的测试用例逐条对照绝大多数问题都能在几分钟内解决。希望这份排查清单能成为你日常调试 sinon-chai 断言失败问题的速查手册。【免费下载链接】sinon-chaiExtends Chai with assertions for the Sinon.JS mocking framework.项目地址: https://gitcode.com/gh_mirrors/si/sinon-chai创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表