已Stub函数仍触发真实调用?Mocha+Sinon测试问题求助
解决Sinon Stub模块内函数不生效的问题
我明白你刚接触Stub/Mock的困惑——明明已经Stub了test1,但测试时还是触发了真实的文件读取操作,这确实很让人头疼。先帮你理清问题出在哪,再给你几个可行的解决办法。
问题根源
先看你的file.js代码:
const fs1 = require('fs'); let param; module.exports = { test, test1, param } // 注意你原来写的是module.export,少了个s,这是个小bug! function test (outputParam) { let fileData = test1(outputParam); // 这里调用的是模块内部的test1函数 param = fileData; } function test1(outputParam) { let data = fs1.readFileSync(outputParam); return data; }
你在测试里Stub的是filejs.test1——也就是导出对象上的test1引用,但test函数内部调用的是模块作用域里的本地test1函数,两者不是同一个引用!所以你的Stub根本没影响到内部的调用逻辑。
解决方案
这里有三种常用的解决思路,你可以根据自己的代码情况选择:
方案1:修改内部调用为导出对象上的方法
最简单的修改方式,让test函数通过module.exports调用test1,这样就和你Stub的是同一个引用了:
function test (outputParam) { let fileData = module.exports.test1(outputParam); // 改成调用导出的test1 param = fileData; }
测试代码不用改,运行时test就会使用你Stub后的test1了。
方案2:重构代码,拆分独立功能到子模块
更符合模块化设计的做法,把test1这类工具函数拆分到单独的模块里,比如新建file-utils.js:
// file-utils.js const fs1 = require('fs'); function test1(outputParam) { let data = fs1.readFileSync(outputParam); return data; } module.exports = { test1 };
然后在file.js里引入这个模块:
// file.js const { test1 } = require('./file-utils'); let param; module.exports = { test, test1, param } function test (outputParam) { let fileData = test1(outputParam); param = fileData; }
测试时Stub子模块的test1即可:
// test.spec.js let sinon = require("sinon"); let filejs = require('./file.js'); let fileUtils = require('./file-utils'); it('should run only the outer function' ,function() { // Stub子模块的test1 sinon.stub(fileUtils,'test1').returns("this is my file content"); // 调用test函数 filejs.test('dummy-arg'); // 验证结果 assert.equal(filejs.param, "this is my file content"); })
这种方式让代码职责更清晰,也更易测试。
方案3:用rewire修改模块内部私有函数
如果不想改动现有代码结构,可以用rewire这个第三方库,它能直接修改模块内部的私有变量/函数:
首先安装依赖:
npm install rewire --save-dev
然后修改测试代码:
// test.spec.js let sinon = require("sinon"); let rewire = require('rewire'); let filejs = rewire('./file.js'); // 用rewire加载模块 it('should run only the outer function' ,function() { // 创建test1的Stub const test1Stub = sinon.stub().returns("this is my file content"); // 替换模块内部的test1函数 filejs.__set__('test1', test1Stub); // 调用test函数 filejs.test('dummy-arg'); // 验证结果 assert.equal(filejs.param, "this is my file content"); // 验证Stub被调用过一次 sinon.assert.calledOnce(test1Stub); })
这种方法不需要修改原业务代码,适合快速解决测试问题。
另外别忘了修正你原代码里的module.export为module.exports,不然模块导出会失效哦!
内容的提问来源于stack exchange,提问作者karansys
相关产品推荐
相关产品推荐

