如何Stub IIFE模块 解决Sinon对IIFE导出模块私有方法打桩不生效问题
打桩失效原因
你定义的Worker是立即执行函数(IIFE)生成的模块,privateFunction1内部直接调用的是闭包作用域内的privateFunction2本身,你导出的对象上挂载的_privateFunction2只是该函数的引用拷贝。Sinon的stub操作替换的是导出对象的_privateFunction2属性指向,并不会修改闭包作用域内原有的privateFunction2变量指向,因此内部调用时仍然会走原生方法,打桩不生效。
解决方案
方案1:修改内部调用逻辑,指向导出对象的属性
在IIFE内部先定义要导出的对象,内部方法调用时统一走导出对象的属性,修改后的Worker模块代码如下:const Worker = (function(){ function start(){...} function stop(){...} function privateFunction1(){ ...some stuff // 改为调用导出对象上的方法,而非闭包内的直接引用 exportObj._privateFunction2(); } function privateFunction2(){...} const exportObj = { start: start, stop: stop, /** for testing only:**/ _privateFunction1: privateFunction1, _privateFunction2: privateFunction2 } return exportObj; })(); module.exports = Worker;该方案无需修改测试代码,原有stub逻辑即可正常生效。
方案2:重构为类写法
把Worker重构为类实例的形式,内部方法调用走this指向实例属性,示例代码如下:class Worker { start() {...} stop() {...} _privateFunction1() { ...some stuff this._privateFunction2(); } _privateFunction2() {...} } module.exports = new Worker();类写法下实例方法天然挂载在实例对象上,Sinon打桩替换实例属性后即可正常拦截内部调用。
方案3:使用rewire库直接修改闭包私有变量
如果你不想修改原有业务代码,可以引入rewire库直接操作模块闭包内的私有变量,测试代码调整如下:const rewire = require('rewire'); // 用rewire引入模块而非原生require const worker = rewire('../../worker'); const sinon = require("sinon"); const chai = require("chai"); var assert = chai.assert; describe("Worker", function () { const sandbox = sinon.createSandbox(); describe("#privateFunction1", function () { let privateFunc2Stub; beforeEach(function () { // 直接替换闭包内的privateFunction2变量 privateFunc2Stub = sandbox.stub(); worker.__set__('privateFunction2', privateFunc2Stub); }); afterEach(function () { sandbox.restore(); }); it("should test something", function () { let obj = {} assert.equal(worker._privateFunction1(obj), false); }); }); });
内容的提问来源于stack exchange,提问作者cagan
相关产品推荐
相关产品推荐

