如何模拟带参数的模块?为pg-promise代码编写单元测试
我来帮你搞定这个pg-promise函数的单元测试!先把你的原代码贴出来,方便我们一起分析:
const pgp = require('pg-promise')(); const cn = { host: process.env.DB_HOST, port: 5432, database: process.env.DB_NAME, user: process.env.DB_USER, password: process.env.DB_PASSWORD }; function insertStuff(stuff) { let db = pgp(cn); return db.one('INSERT INTO test(stuff) VALUES ($1) RETURNING id, stuff', [stuff]) .then(data => { return data }) } module.exports.insertStuff = insertStuff
单元测试的核心是隔离真实数据库依赖,所以我们要mock掉pg-promise的数据库连接和查询行为,这样既能验证代码逻辑,又不会污染真实数据。下面给你两种常用的测试实现方案:
方案一:用Jest + Sinon实现Mock测试
首先安装依赖:
npm install --save-dev jest sinon
然后编写测试文件(比如stuff.test.js):
const sinon = require('sinon'); const pgp = require('pg-promise'); const { insertStuff } = require('./your-file-name'); // 替换成你的实际文件名 describe('insertStuff function', () => { let mockDb; let mockQuery; // 每个测试前初始化mock beforeEach(() => { // 模拟db.one方法,可控制返回成功/失败 mockQuery = sinon.stub(); // 模拟pg-promise创建的数据库实例 mockDb = { one: mockQuery }; // 替换pg-promise的构造函数,让它返回我们的mock实例 sinon.stub(pgp.prototype, 'constructor').returns(mockDb); }); // 每个测试后清理mock,避免影响其他用例 afterEach(() => { sinon.restore(); }); test('✅ 正常插入数据时,返回正确的结果', async () => { const testData = 'test-stuff'; // 模拟数据库成功返回的数据 mockQuery.resolves({ id: 100, stuff: testData }); const result = await insertStuff(testData); // 验证查询方法被调用了一次 expect(mockQuery.calledOnce).toBe(true); // 验证SQL语句和参数完全匹配 expect(mockQuery.calledWith( 'INSERT INTO test(stuff) VALUES ($1) RETURNING id, stuff', [testData] )).toBe(true); // 验证返回结果符合预期 expect(result).toEqual({ id: 100, stuff: testData }); }); test('❌ 数据库查询失败时,正确抛出错误', async () => { const testError = new Error('数据库连接超时'); // 模拟数据库查询失败 mockQuery.rejects(testError); // 验证函数会抛出对应的错误 await expect(insertStuff('bad-data')).rejects.toThrow(testError); expect(mockQuery.calledOnce).toBe(true); }); });
方案二:用Jest原生Mock实现(无需额外安装Sinon)
如果不想用Sinon,Jest自带的mock功能也能搞定:
const pgp = require('pg-promise'); const { insertStuff } = require('./your-file-name'); // 替换成你的实际文件名 // 全局mock pg-promise包 jest.mock('pg-promise', () => { const mockQuery = jest.fn(); const mockDb = { one: mockQuery }; // 返回一个模拟的pg-promise构造函数 return jest.fn(() => mockDb); }); describe('insertStuff function', () => { let mockQuery; beforeEach(() => { mockQuery = pgp().one; // 重置mock的调用记录 mockQuery.mockReset(); }); test('✅ 插入数据时正确执行SQL并返回结果', async () => { mockQuery.mockResolvedValue({ id: 200, stuff: 'jest-test' }); const result = await insertStuff('jest-data'); expect(mockQuery).toHaveBeenCalledWith( 'INSERT INTO test(stuff) VALUES ($1) RETURNING id, stuff', ['jest-data'] ); expect(result).toEqual({ id: 200, stuff: 'jest-test' }); }); test('❌ 处理查询失败的场景', async () => { const mockError = new Error('SQL语法错误'); mockQuery.mockRejectedValue(mockError); await expect(insertStuff('invalid-data')).rejects.toThrow(mockError); }); });
额外提示
- 如果你的代码依赖环境变量,可以在测试文件开头引入
dotenv加载配置:require('dotenv').config(),不过因为我们mock了数据库连接,实际不会真的连接数据库。 - 单元测试只需要验证代码逻辑是否正确:比如SQL语句是否符合预期、参数是否正确传递、错误是否正确处理,不需要测试数据库本身的功能。
内容的提问来源于stack exchange,提问作者user1584120
相关产品推荐
相关产品推荐

