如何使用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
相关产品推荐
相关产品推荐

