You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.26 09:58:43