使用Sinon模拟AWS DynamoDB连接时测试失败问题排查
解决Lambda处理函数测试中的两个关键问题
1. 修复「Resolution method is overspecified」错误
这个错误是因为测试用例同时使用了async函数和done回调,Mocha不允许同时采用这两种异步测试方式。直接移除done参数及所有done()调用,依赖async/await本身的Promise机制即可:
it("Test saveUser() method", async function() { // 移除done参数 const { saveUser } = userHandler; // ... 其他测试代码 try { const result = await saveUser("Sample User", { log: () => {}, exception: () => {} }); // 注意:对象比较要用deep.equal,而非equal expect(result).to.deep.equal({ statusCode: 201, body: { userName: "Sample User", ttl: sinon.match.number, id: sinon.match.string } }); } catch (err) { console.log(err); throw err; // 抛出错误让Mocha捕获并标记测试失败 } });
2. 修复Sinon Stub不生效的问题(阻止真实DynamoDB连接)
你测试代码中创建的db实例,和Handler.js里使用的db实例不是同一个对象,因此stub测试里的db.put根本不会影响到handler内的调用。有两种解决方式:
方法一:依赖注入(推荐)
修改Handler.js的saveUser方法,允许传入db实例作为参数,测试时传入stub后的实例:
// Handler.js import AWS from "aws-sdk"; import uuid from "uuid"; // 定义默认DB实例 const defaultDb = new AWS.DynamoDB.DocumentClient({ apiVersion: "2012-08-10" }); saveUser(userName, logger, db = defaultDb) { // 新增db参数,默认使用实例 const Item = { id: uuid.v4(), userName, ttl: parseInt(Date.now() / 1000) + 900 }; const params = { TableName: "my-table-name", Item }; logger.log(`Saving new user name to DynamoDB: ${JSON.stringify(params)}`); return new Promise(function(resolve, reject) { db.put(params, function(err, _) { if (err) { logger.exception(`Unable to connect to DynamoDB to create: ${err}`); reject({ statusCode: 404, err }); } else { logger.log(`Saved data to DynamoDB: ${JSON.stringify(Item)}`); resolve({ statusCode: 201, body: Item }); } }); }); }
对应的测试代码:
// Handler.spec.js import AWS from "aws-sdk"; const db = new AWS.DynamoDB.DocumentClient({ apiVersion: "2012-08-10" }); describe("user-name-handler", function() { const sandbox = sinon.createSandbox(); afterEach(() => sandbox.restore()); it("Test saveUser() method", async function() { const { saveUser } = userHandler; // 模拟db.put的回调行为,而非返回Promise sandbox.stub(db, "put").callsFake((params, callback) => { callback(null, {}); // 触发成功回调 }); const result = await saveUser("Sample User", { log: () => {}, exception: () => {} }, db); // 传入stub后的db实例 expect(result.statusCode).to.equal(201); expect(result.body.userName).to.equal("Sample User"); // 验证put方法被调用过一次 sinon.assert.calledOnce(db.put); }); });
方法二:Stub AWS.DynamoDB.DocumentClient的原型
如果不想修改handler代码,可以直接Stub DocumentClient的原型方法,让所有实例的put方法都被替换:
// Handler.spec.js import AWS from "aws-sdk"; describe("user-name-handler", function() { const sandbox = sinon.createSandbox(); afterEach(() => sandbox.restore()); it("Test saveUser() method", async function() { const { saveUser } = userHandler; // Stub DocumentClient原型的put方法 sandbox.stub(AWS.DynamoDB.DocumentClient.prototype, "put").callsFake((params, callback) => { callback(null, {}); }); const result = await saveUser("Sample User", { log: () => {}, exception: () => {} }); expect(result.statusCode).to.equal(201); sinon.assert.calledOnce(AWS.DynamoDB.DocumentClient.prototype.put); }); });
额外注意事项
- 原测试中的断言
expect(result).to.be.equal({ data: "some data" })与实际返回值结构不匹配,需修正为对应{ statusCode:201, body: Item }的断言逻辑。 db.put是回调风格方法,Stub时必须用callsFake模拟回调触发,而非returns返回Promise,否则handler内的回调不会执行。
内容的提问来源于stack exchange,提问作者deekeh
相关产品推荐
相关产品推荐

