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

使用Sinon模拟Knex查询构建器的Node.js单元测试问题

解决Knex查询构建器的Sinon模拟问题

问题根源

你遇到的queryObject is not a function错误,本质是因为Knex查询构建器是链式调用的对象而非函数。如果模拟时没有正确维护链式调用的返回关系(每个方法返回可继续调用的查询对象),或者没有让模拟对象支持await(即实现thenable接口),就会触发这类错误。

解决方案

核心思路是用Sinon创建一个链式Stub,模拟Knex查询构建器的所有调用方法,确保每个方法返回Stub自身以维持链式调用,同时让Stub支持异步await逻辑。

完整测试代码示例

假设待测试函数在myModule.js中,测试文件代码如下:

const { expect } = require('chai');
const sinon = require('sinon');
const myFunction = require('./myModule');

describe('待测试函数', () => {
  let databaseStub;
  let queryBuilderStub;

  // 每个测试前重置Stub
  beforeEach(() => {
    // 1. 创建查询构建器Stub,维护链式调用
    queryBuilderStub = sinon.stub();
    // 每个链式方法都返回Stub自身
    queryBuilderStub.withSchema.returns(queryBuilderStub);
    queryBuilderStub.select.returns(queryBuilderStub);
    queryBuilderStub.where.returns(queryBuilderStub);
    // 模拟异步查询成功返回数据
    queryBuilderStub.resolves([
      { some_col_1: 'test_val', some_col_2: 'foo', some_col_3: 'bar' }
    ]);

    // 2. 创建database Stub,table方法返回查询构建器Stub
    databaseStub = sinon.stub();
    databaseStub.table.returns(queryBuilderStub);
  });

  // 每个测试后清理Stub
  afterEach(() => {
    sinon.restore();
  });

  it('传入data时,应构建包含where子句的查询', async () => {
    const testData = { something: 'target_val' };
    await myFunction({ database: databaseStub, data: testData });

    // 断言各方法调用正确性
    expect(databaseStub.table.calledWith('nice')).to.be.true;
    expect(queryBuilderStub.withSchema.calledWith('very')).to.be.true;
    expect(queryBuilderStub.select.calledWith(['some_col_1', 'some_col_2', 'some_col_3'])).to.be.true;
    expect(queryBuilderStub.where.calledWith('some_col_1', testData.something)).to.be.true;
  });

  it('未传入data时,不应添加where子句', async () => {
    await myFunction({ database: databaseStub, data: null });

    expect(queryBuilderStub.where.called).to.be.false;
  });

  it('查询出错时,应捕获并打印错误', async () => {
    const testError = new Error('数据库连接失败');
    // 模拟查询失败
    queryBuilderStub.rejects(testError);
    // 捕获console.log调用
    const consoleLogStub = sinon.stub(console, 'log');

    await myFunction({ database: databaseStub, data: null });

    expect(consoleLogStub.calledWith(testError)).to.be.true;
    consoleLogStub.restore();
  });
});

关键细节说明

  • 链式Stub维护:每个查询方法(withSchema/select/where)都通过returns(queryBuilderStub)返回自身,确保链式调用不会中断。
  • 异步模拟:用resolves()/rejects()模拟查询的成功/失败结果,让Stub支持await操作。
  • Stub清理:在afterEach中调用sinon.restore()避免测试间的Stub污染。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.20 16:23:27