如何用Sinon Stub Node.js(Hapi.js)中require的嵌套子模块?
解决你的Sinon单元测试问题:模块找不到+嵌套依赖Stub
先帮你搞定那个头疼的Cannot find module './library'错误,再教你怎么用Sinon配合工具Stub掉index.js里的library.js,包括它的嵌套第三方依赖。
第一步:修复模块找不到的错误
这个报错大概率是路径或工作目录的问题,你可以先检查这两点:
- 确认
library.js和index.js真的在同一个文件夹下,别打错文件名(注意大小写,Node.js在Linux/macOS下区分文件名大小写) - 运行测试命令时的工作目录要正确:比如如果测试文件在
test/子文件夹里,别跑到test/目录下执行命令,应该回到项目根目录运行mocha test/testfunc.js这类指令,否则index.js里的./library.js会相对于错误的目录查找文件。
先把这个基础问题解决,再处理Stub的需求。
第二步:用Sinon+Proxyquire Stub嵌套依赖
Sinon本身没法直接替换模块内部已经require进来的依赖,得靠Proxyquire这个工具帮忙拦截模块的依赖加载。它能让我们在加载index.js时,把它依赖的library.js换成Sinon制作的Stub,甚至连library.js里的第三方依赖googlelib也能一起Stub掉。
先安装Proxyquire
先安装这个开发依赖:
npm install proxyquire --save-dev
两种Stub方式,按需选择
假设你的代码结构大概是这样(方便你对应自己的代码):
// index.js const library = require('./library.js'); exports.runTask = () => { return library.processData(); };
// library.js const googlelib = require('googlelib'); exports.processData = () => { return googlelib.fetchData(); };
方式1:连嵌套的googlelib一起Stub
如果需要验证library.js是否调用了googlelib的方法,就用这种方式:
const sinon = require('sinon'); const proxyquire = require('proxyquire'); describe('index.js的runTask方法', () => { it('应该能Stub library和它依赖的googlelib', () => { // 1. 先给googlelib做Stub,模拟它的返回值 const googlelibStub = { fetchData: sinon.stub().returns('模拟的第三方返回数据') }; // 2. 用Proxyquire加载library.js,把它的googlelib换成上面的Stub const libraryStub = proxyquire('./library.js', { 'googlelib': googlelibStub }); // 3. 再加载index.js,把它的library换成我们做好的Stub const index = proxyquire('./index.js', { './library.js': libraryStub }); // 4. 调用方法并验证 const result = index.runTask(); sinon.assert.calledOnce(googlelibStub.fetchData); // 验证googlelib的方法被调用 console.log(result); // 会输出'模拟的第三方返回数据' }); });
方式2:直接Stub library.js的方法
如果你不需要关心library.js内部怎么调用googlelib,只需要模拟library.js的返回结果,那就简单多了:
const sinon = require('sinon'); const proxyquire = require('proxyquire'); describe('index.js的runTask方法', () => { it('直接Stub library.js就行', () => { // 给library做Stub,模拟processData方法的返回 const libraryStub = { processData: sinon.stub().returns('直接模拟的library返回值') }; // 加载index.js,替换它的library依赖 const index = proxyquire('./index.js', { './library.js': libraryStub }); const result = index.runTask(); sinon.assert.calledOnce(libraryStub.processData); // 验证library的方法被调用 console.log(result); // 输出'直接模拟的library返回值' }); });
为什么不能只用Sinon?
因为Node.js的require会缓存加载过的模块,当你直接require('./index.js')时,它已经把真实的library.js加载并缓存了,Sinon没法替换已经缓存的模块。Proxyquire的作用就是在加载模块的那一刻,拦截它的require调用,把指定的依赖换成我们的Stub,这样就能完全隔离外部依赖,专注测试index.js的核心逻辑了。
内容的提问来源于stack exchange,提问作者J123
相关产品推荐
相关产品推荐

