如何用Sinon Mock Knex查询?转换测试遇属性不存在错误
解决Knex查询函数Mock时的"Cannot stub non-existent own property"错误
看起来你遇到的问题核心是:getDataById并非某个对象的自有可Stub属性——要么它是直接导出的独立函数,要么你的导入/Stub方式不对,导致Sinon找不到可操作的目标。结合你的原有测试代码,我给你几种可行的解决方案:
方案1:用Proxyquire替换模块导出(推荐)
如果getDataById是单独导出的(比如module.exports = getDataById),或是属于某个模块的导出对象,Proxyquire可以帮你在测试时无缝替换掉这个函数的实现,完美避开Stub非自有属性的问题。
示例代码:
const proxyquire = require('proxyquire'); const sinon = require('sinon'); const { expect } = require('chai'); describe('Data Get', () => { let getDataByIdStub; let dataService; before(() => { // 创建Stub并预设返回的测试数据 getDataByIdStub = sinon.stub().resolves(yourMockData); // 用Proxyquire加载目标模块,替换掉getDataById // 假设你的getDataById在./data-service.js中,根据导出方式调整: // 情况1:默认导出函数 → './data-service': getDataByIdStub // 情况2:导出对象 → './data-service': { getDataById: getDataByIdStub } dataService = proxyquire('./data-service', { './data-service': getDataByIdStub }); }); it('Should return correct data for valid ID', async () => { const result = await dataService.getDataById({ id: validId }); expect(result).to.deep.equal(expectedData); // 验证Stub被正确调用 sinon.assert.calledWith(getDataByIdStub, { id: validId }); }); });
方案2:直接Stub模块导出对象(对象导出场景)
如果getDataById是作为对象的方法导出的(比如module.exports = { getDataById }),你可以直接Stub这个导出对象上的方法,前提是测试中导入的是这个对象:
示例代码:
const sinon = require('sinon'); const { expect } = require('chai'); const dataService = require('./data-service'); describe('Data Get', () => { let getDataByIdStub; before(() => { // 直接Stub导出对象上的getDataById方法 getDataByIdStub = sinon.stub(dataService, 'getDataById').resolves(yourMockData); }); after(() => { // 测试结束后恢复原方法 dataService.getDataById.restore(); }); it('Should return correct data for valid ID', async () => { const result = await dataService.getDataById({ id: validId }); expect(result).to.deep.equal(expectedData); sinon.assert.calledWith(getDataByIdStub, { id: validId }); }); });
方案3:Mock Knex查询链(验证查询逻辑)
如果你想验证Knex查询的正确性(比如表名、字段、查询条件是否符合预期),可以直接Mock Knex的链式调用:
示例代码:
const sinon = require('sinon'); const { expect } = require('chai'); const knex = require('./your-knex-instance'); const dataService = require('./data-service'); describe('Data Get', () => { let knexFromStub; let selectStub; let whereStub; before(() => { // 构建Knex查询链的Stub whereStub = sinon.stub().resolves(yourMockData); selectStub = sinon.stub().returns({ where: whereStub }); knexFromStub = sinon.stub(knex, 'from').returns({ select: selectStub }); }); after(() => { // 恢复Knex原方法 knex.from.restore(); }); it('Should build correct Knex query and return data', async () => { const result = await dataService.getDataById({ id: validId }); expect(result).to.deep.equal(expectedData); // 验证查询链的参数是否正确 sinon.assert.calledWith(knexFromStub, 'data'); sinon.assert.calledWith(selectStub, ['a AS A', 'b as B']); sinon.assert.calledWith(whereStub, 'id', validId); }); });
为什么会出现这个报错?
你之前的代码是Stubconn.executeQuery——这是一个明确的对象自有属性方法,Sinon可以直接识别操作。但现在你尝试Stub的getDataById要么是独立导出的函数(不属于任何对象的属性),要么导入方式错误(比如直接导入函数本身而非包含它的对象),导致Sinon无法将其识别为可Stub的目标。
你可以根据测试需求选择方案:
- 只想验证业务逻辑 → 方案1/2更简单高效;
- 要验证Knex查询的正确性 → 方案3更贴合真实调用场景。
内容的提问来源于stack exchange,提问作者NodeNoob
相关产品推荐
相关产品推荐

