Node.js中如何用Sinon Stub内联require类对象及依赖注入?
嘿,这个inline require的Stub问题我太熟了!嵌套依赖的Stub确实容易卡壳,不过用对工具就很简单,我给你两个实用方案,直接上代码:
先把你的示例代码补全一下,方便对照:
// query-helper.js var logger = require('./log/logger.js').getDefaultLogger(); class QueryHelper { static save(data) { logger.info('Saving data:', data); // 业务逻辑 } } module.exports = QueryHelper;
// abc-handler.js var QueryHelper = require('./query-helper'); var logger = require('./log/logger.js').getDefaultLogger(); class ABCHandler { static processRequest(req) { logger.debug('Processing request:', req); QueryHelper.save(req.body); // 业务逻辑 } } module.exports = ABCHandler;
方案1:用
proxyquire一键替换所有依赖的logger proxyquire可以在加载模块时,直接替换它内部require的任意依赖,包括嵌套的(比如QueryHelper里的logger)。
首先安装依赖:
npm install proxyquire sinon --save-dev
然后写测试代码:
const sinon = require('sinon'); const proxyquire = require('proxyquire'); describe('ABCHandler', () => { it('should stub logger in both ABCHandler and QueryHelper', () => { // 先创建一个logger的Stub,模拟所有需要用到的方法 const loggerStub = { info: sinon.spy(), debug: sinon.spy() }; // 用proxyquire加载ABCHandler,同时替换它和QueryHelper依赖的logger const ABCHandler = proxyquire('./abc-handler', { // 替换ABCHandler里直接require的logger './log/logger.js': { getDefaultLogger: sinon.stub().returns(loggerStub) }, // 替换QueryHelper里的logger(嵌套依赖) './query-helper': proxyquire('./query-helper', { './log/logger.js': { getDefaultLogger: sinon.stub().returns(loggerStub) } }) }); // 执行测试方法 ABCHandler.processRequest({ body: { id: 1 } }); // 验证Stub是否被正确调用 sinon.assert.calledOnce(loggerStub.debug); sinon.assert.calledOnce(loggerStub.info); sinon.assert.calledWith(loggerStub.info, 'Saving data:', { id: 1 }); }); });
方案2:用
rewire直接修改模块内部变量 rewire能让你访问模块的私有变量(包括require进来的logger),适合更精细的控制。
先安装依赖:
npm install rewire sinon --save-dev
测试代码示例:
const sinon = require('sinon'); const rewire = require('rewire'); describe('ABCHandler with rewire', () => { it('should stub logger in ABCHandler and QueryHelper', () => { const loggerStub = { info: sinon.spy(), debug: sinon.spy() }; // 先处理QueryHelper,替换它内部的logger变量 const queryHelperRewire = rewire('./query-helper'); queryHelperRewire.__set__('logger', loggerStub); // 再处理ABCHandler,替换它自己的logger和依赖的QueryHelper const abcHandlerRewire = rewire('./abc-handler'); abcHandlerRewire.__set__('logger', loggerStub); abcHandlerRewire.__set__('QueryHelper', queryHelperRewire); // 执行测试 abcHandlerRewire.processRequest({ body: { id: 2 } }); // 验证调用情况 sinon.assert.calledOnce(loggerStub.debug); sinon.assert.calledOnce(loggerStub.info); sinon.assert.calledWith(loggerStub.info, 'Saving data:', { id: 2 }); }); });
小提示
- 如果你的项目用的是ES模块(
import/export),可以换成esmock代替proxyquire,用法类似 - 每个测试结束后最好恢复原始依赖,避免影响其他测试(比如用
sinon.restore()或者rewire的__restore__方法)
内容的提问来源于stack exchange,提问作者quartaela
相关产品推荐
相关产品推荐

