使用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映射的新对象 } }
错误原因
- Stub时机错误:你在调用
lambda.handler()之后才执行stub,此时handler内部已经完成了database.getRecordDetails()的调用,stub根本没生效,顺序完全搞反。 - Stub对象错误:
getRecordDetails是Database类的实例方法(挂载在类的原型上),不是类的静态方法。直接尝试stubdb.getRecordDetails(db是导入的类本身),但这个属性不存在,因此报错。 - 异步方法处理遗漏:原方法是
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,提问作者夢のの夢
相关产品推荐
相关产品推荐

