Stub第三方库函数后仍抛出ENOENT错误,如何解决?
问题概述
测试自定义函数时,无法正确Stub其内部调用的第三方库函数。
我的代码
myCustomFunctions.js中的代码:
function myFunc(resource) { const thirdPartyFunc = require('third-party-lib') try { let thirdPartyFuncOutput = thirdPartyFunc(resource.path); return; } catch (e) { vscode.window.showErrorMessage(`Something went wrong: ${e}`); return; } }
我的测试代码
测试套件代码:
const myCustomFunctions = require('../../myCustomFunctions.js'); suite('testing myCustomFunctions.js', function () { setup(function () { sandbox = sinon.createSandbox(); mockedInput = { path: 'something', options: {} }; }); teardown(function () { sandbox.restore(); }); test('Should generate expected output for valid resource', async function () { const expectedOutput = 'Hi Mom'; const thirdPartyFuncStub = sandbox.stub().returns(expectedOutput); // 尝试替换第三方方法的响应 myCustomFunctions.thirdPartyFunc = thirdPartyFuncStub await myCustomFunctions.myFunc(mockedInput); // 断言逻辑 }); })
问题现象
测试执行到let thirdPartyFuncOutput = thirdPartyFunc(resource.path);时,未返回Mock值,反而抛出错误:
Error: ENOENT: no such file or directory, lstat 'something'
明明创建了Stub,原函数却仍在处理真实输入,Stub未生效的原因是什么?
环境信息
- node v18.16.1
- mocha v10.2.0
- sinon v15.2.0
解决方案
核心原因
你在myFunc内部调用require('third-party-lib'),意味着每次执行myFunc都会重新加载第三方库模块,而不是复用模块缓存或你在测试中替换的引用。测试中给myCustomFunctions.thirdPartyFunc赋值的Stub,和函数内部的thirdPartyFunc是完全独立的两个引用,Stub自然无法生效。
修复步骤
方法1:将第三方库的require移到模块顶部,导出依赖(推荐)
修改myCustomFunctions.js,把require移到模块外层,模块加载时只会执行一次require,测试时可直接Stub模块导出的引用:
// 移到模块顶部,仅加载一次 const thirdPartyFunc = require('third-party-lib'); function myFunc(resource) { try { let thirdPartyFuncOutput = thirdPartyFunc(resource.path); return; } catch (e) { vscode.window.showErrorMessage(`Something went wrong: ${e}`); return; } } // 导出函数和依赖,方便测试时Stub module.exports = { myFunc, thirdPartyFunc };
测试代码中,直接Stub模块导出的thirdPartyFunc:
const myCustomFunctions = require('../../myCustomFunctions.js'); suite('testing myCustomFunctions.js', function () { // ... 其他代码不变 test('Should generate expected output for valid resource', async function () { const expectedOutput = 'Hi Mom'; // 直接Stub模块导出的thirdPartyFunc sandbox.stub(myCustomFunctions, 'thirdPartyFunc').returns(expectedOutput); await myCustomFunctions.myFunc(mockedInput); // 示例断言:验证第三方函数被正确调用 sinon.assert.calledWith(myCustomFunctions.thirdPartyFunc, 'something'); }); })
方法2:操作Node.js模块缓存Stub(侵入性强,不推荐)
如果不想修改原代码结构,可以通过Node.js的模块缓存机制Stub:
test('Should generate expected output for valid resource', async function () { const expectedOutput = 'Hi Mom'; // 获取第三方模块的缓存引用 const thirdPartyLib = require('third-party-lib'); // 根据模块导出类型Stub(默认导出用default,命名导出用对应属性名) sandbox.stub(thirdPartyLib, 'default').returns(expectedOutput); await myCustomFunctions.myFunc(mockedInput); // 断言逻辑 });
这种方法依赖Node.js内部机制,代码可读性差,不适合长期维护的项目。
额外注意点
- 原代码中
thirdPartyFunc(path)的path未定义,需修正为resource.path,否则会触发path is not defined错误。 - 测试代码中模块引入的变量名要统一,原代码里
const myFunc = require(...)和后续myCustomFunctions.myFunc的变量名不一致,需修正。
内容的提问来源于stack exchange,提问作者null-point-exceptional
相关产品推荐
相关产品推荐

