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

如何模拟带参数的模块?为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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.20 12:23:47