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

如何使用Sinon.js伪造pg的Pool连接,实现单元测试不访问真实数据库

实现方案

你无需修改原有业务代码,只需要在单元测试中通过Sinon配合依赖替换,即可完全mock Pool连接逻辑,避免访问真实数据库,具体实现如下:

方案1:通过proxyquire替换pg依赖(无需修改原代码)

假设你的原有数据层文件名为db.js,测试文件编写逻辑如下:

const sinon = require('sinon');
const proxyquire = require('proxyquire');
const assert = sinon.assert;

describe('queryWithParameter 单元测试', () => {
  let queryStub;
  let releaseStub;
  let connectStub;
  let poolStub;
  let loggerStub;
  let db;

  beforeEach(() => {
    // 1. Mock数据库客户端的查询方法和连接释放方法
    queryStub = sinon.stub();
    releaseStub = sinon.stub();
    // 2. Mock Pool的connect方法,返回上面构造的假客户端
    connectStub = sinon.stub().resolves({
      query: queryStub,
      release: releaseStub
    });
    // 3. Mock Pool构造函数,实例化后返回带假connect方法的对象
    poolStub = sinon.stub().returns({
      connect: connectStub
    });
    // 4. Mock logger避免测试输出冗余日志
    loggerStub = { error: sinon.stub() };
    // 5. 加载db.js时替换掉依赖的pg和logger模块
    db = proxyquire('./db.js', {
      'pg': { Pool: poolStub },
      './logger.js': loggerStub
    });
  });

  afterEach(() => {
    // 每次用例执行完重置所有stub,避免用例互相污染
    sinon.restore();
  });

  it('无参数查询时返回预期结果', async () => {
    // 构造假返回值
    const mockRows = [{ id: 1, name: '测试数据' }];
    queryStub.resolves({ rows: mockRows });

    const res = await db.queryWithParameter('SELECT * FROM users', null);

    // 断言逻辑
    assert.calledOnce(poolStub);
    assert.calledOnce(connectStub);
    assert.calledWithExactly(queryStub, 'SELECT * FROM users');
    assert.calledOnce(releaseStub);
    assert.match(res, mockRows);
  });

  it('带参数查询时正确传递参数', async () => {
    const mockRows = [{ id: 2, name: '指定用户' }];
    const testSql = 'SELECT * FROM users WHERE id = $1';
    const testParams = [2];
    queryStub.resolves({ rows: mockRows });

    const res = await db.queryWithParameter(testSql, testParams);

    assert.calledWithExactly(queryStub, testSql, testParams);
    assert.match(res, mockRows);
  });

  it('查询报错时正确打印错误日志', async () => {
    const testError = new Error('表不存在');
    queryStub.rejects(testError);

    const res = await db.queryWithParameter('SELECT * FROM not_exist_table', null);

    assert.calledWithExactly(loggerStub.error, 'error in queryWithParameter : ' + testError);
    assert.match(res, undefined);
  });
});

方案2:直接stub导出的pool实例(更简单,需修改少量原代码)

在原有db.js的导出部分新增导出pool实例:

module.exports = {
  queryWithParameter,
  pool // 新增该行导出pool实例
};

之后测试文件可以不用proxyquire,直接stub导出的pool即可:

const sinon = require('sinon');
const db = require('./db.js');
const assert = sinon.assert;

describe('queryWithParameter 单元测试', () => {
  let queryStub;
  beforeEach(() => {
    queryStub = sinon.stub().resolves({ rows: [] });
    const releaseStub = sinon.stub();
    // 直接stub pool的connect方法
    sinon.stub(db.pool, 'connect').resolves({
      query: queryStub,
      release: releaseStub
    });
  });

  afterEach(() => sinon.restore());

  // 后续测试用例逻辑和方案1完全一致
});

内容的提问来源于stack exchange,提问作者JPOX

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.04 02:12:02