如何在Sinon中stub通过解构require导入的函数且不修改原文件引入方式
问题根因
CommonJS的模块加载是运行时执行的,fileB 中通过解构 const { doSomething } = require('./fileA') 导入时,会直接将原doSomething函数的引用复制到fileB的作用域中。你原测试代码中先加载了fileB再stub fileA上的方法,只是修改了fileA导出对象的属性指向,并没有修改fileB中已经保存的原函数引用,因此stub不生效。
解决方案1:调整模块加载顺序(无需额外依赖)
只要在加载fileB之前就完成对fileA对应方法的stub,此时fileB解构拿到的就是stub后的函数引用:
const sinon = require('sinon'); const { expect } = require('chai'); const fileA = require('./fileA'); // 先stub fileA的方法,再加载fileB const fileAStub = sinon.stub(fileA, 'doSomething').returns(15); const fileB = require('./fileB'); describe('Test File B', function(){ afterEach(function() { // 测试结束恢复原方法,避免影响其他用例 fileAStub.restore(); // 清除模块缓存,避免其他测试加载到缓存的fileB delete require.cache[require.resolve('./fileB')]; }) it('stub生效示例', function() { expect(fileB.doAnotherThing()).to.equal(25); }) })
解决方案2:使用proxyquire(更灵活,无全局污染)
proxyquire是专门用于替换NodeJS模块依赖的工具,可以在加载目标模块时临时替换它的依赖,不会修改全局模块缓存,更适合多测试用例的场景:
const sinon = require('sinon'); const { expect } = require('chai'); const proxyquire = require('proxyquire'); describe('Test File B', function(){ it('stub生效示例', function() { // 构造替换用的stub const fileAStub = sinon.stub().returns(15); // 加载fileB时,将它依赖的./fileA替换为我们自定义的对象 const fileB = proxyquire('./fileB', { './fileA': { doSomething: fileAStub } }); expect(fileB.doAnotherThing()).to.equal(25); }) })
注意事项
- 方案1需要手动处理模块缓存清除和stub恢复,否则会造成测试用例之间的相互影响
- 方案2不需要额外处理缓存,每次调用
proxyquire加载的模块都是独立实例,更推荐在复杂测试场景下使用
内容的提问来源于stack exchange,提问作者Zongwei Kay
相关产品推荐
相关产品推荐

