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

使用Sinon存根Database类getRecordDetails方法时报错:无法存根不存在的自有属性

解决TypeError: Cannot stub non-existent own property getRecordDetails

我尝试mock Database模块的getRecordDetails()成员函数使其返回固定数据,但运行测试时遇到错误:

TypeError: Cannot stub non-existent own property getRecordDetails

相关代码如下:

代码文件

handler.js

const Database = require("../../lib/Database");
const database= new Database();

module.exports = {
  handler(event, context, callback) {
    event.Records.forEach(record=> {
       const recordDetails = database.getRecordDetails(record);
       // 处理recordDetails
    });
  }
}

test.js

const sinon = require("sinon");
const db= require("../../lib/Database");
const lambda = require("../../lib/lambda.js");

describe("Test suite", () => {
  it("invoke lambda",  done => {
    lambda.handler(data, {}, (err, data) => {
      sinon.stub(db, "getRecordDetails").withArgs(sinon.match.any).returns("{id: 1}");
      ...
    })
  })
})

database.js

class Database {
  constructor() {
    this.dynamdbClient = new AWS.DynamoDB.DocumentClient();
  }

  async getRecordDetails(record) {
    const param = {
      TableName: ..
      Key: {
        uuid: record.uuid
        ...
     }
    }
    const dbRecord = await this.dynamdbClient.get(params).promise();
    return mapToRecordDetails(dbRecord); // 1-1映射的新对象
  }
}

错误原因

  1. Stub时机错误:你在调用lambda.handler()之后才执行stub,此时handler内部已经完成了database.getRecordDetails()的调用,stub根本没生效,顺序完全搞反。
  2. Stub对象错误:getRecordDetails是Database类的实例方法(挂载在类的原型上),不是类的静态方法。直接尝试stubdb.getRecordDetails(db是导入的类本身),但这个属性不存在,因此报错。
  3. 异步方法处理遗漏:原方法是async函数,用returns直接返回字符串会导致handler里拿到未处理的Promise对象,而非预期数据。

修正方案

方案一:Stub Database类的原型方法(无需修改handler)

调整test.js代码,确保在加载lambda模块前完成stub,并且针对类的原型操作:

const sinon = require("sinon");
const Database = require("../../lib/Database");

// 先stub原型上的getRecordDetails方法
sinon.stub(Database.prototype, "getRecordDetails")
  .withArgs(sinon.match.any)
  .resolves({ id: 1 }); // 异步方法用resolves返回Promise

// 再加载lambda模块,确保handler里的database实例使用已stub的方法
const lambda = require("../../lib/lambda.js");

describe("Test suite", () => {
  it("invoke lambda", done => {
    // 直接调用handler,此时stub已经生效
    lambda.handler(data, {}, (err, result) => {
      // 这里添加断言逻辑
      Database.prototype.getRecordDetails.restore(); // 测试后恢复原方法
      done();
    });
  });
});

方案二:依赖注入(更优雅的测试方式)

修改handler.js,让其接受Database实例作为参数,测试时直接传入mock对象:

修改后的handler.js

const Database = require("../../lib/Database");

module.exports = {
  // 新增可选参数,默认使用新实例
  handler(event, context, callback, database = new Database()) {
    event.Records.forEach(record=> {
       const recordDetails = database.getRecordDetails(record);
       // 处理recordDetails
    });
  }
}

修改后的test.js

const sinon = require("sinon");
const lambda = require("../../lib/lambda.js");

describe("Test suite", () => {
  it("invoke lambda", done => {
    // 创建mock的database实例
    const mockDb = {
      getRecordDetails: sinon.stub().withArgs(sinon.match.any).resolves({ id: 1 })
    };

    // 调用handler时传入mock实例
    lambda.handler(data, {}, (err, result) => {
      // 断言mock方法被调用
      sinon.assert.called(mockDb.getRecordDetails);
      done();
    }, mockDb);
  });
});

关键注意点

  • 异步方法必须用resolves()(而非returns())返回模拟数据,否则会得到未处理的Promise。
  • Stub操作必须在被测试代码加载/执行前完成,确保实例化时使用已被stub的方法。
  • 测试完成后记得恢复原方法,避免影响其他测试用例。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 11:23:20