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

