如何测试操作AWS DynamoDB的JS方法?解决测试回调与Promise冲突报错
解决AWS Lambda DynamoDB测试报错及无真实调用测试实现
问题分析
你遇到的Error: Resolution method is overspecified.是因为测试用例同时使用了async函数和done回调参数,Mocha不允许这种混合写法。另外,你的DynamoDB Stub方式不对——DocumentClient.put是基于回调的API,不是返回Promise的,还有Handler代码里存在参数缺失的问题。
修复步骤
1. 移除测试用例的done参数
async函数本身会返回Promise,Mocha会自动等待Promise完成,不需要额外传入done回调。
2. 正确Stub DynamoDB的put方法
使用sinon的yields方法模拟回调执行:
- 模拟成功:
sandbox.stub(db, 'put').yields(null, {}),第一个参数为null表示无错误,第二个参数是回调返回值 - 模拟失败:
sandbox.stub(db, 'put').yields(new Error('DB error'), null)
3. 修正Handler中的参数错误
原代码里的params仅包含TableName,缺少Item字段,这会导致真实调用DynamoDB时失败,需要补上该字段。
修正后的完整代码
Handler.js
const uuid = require('uuid'); // 假设db是外部引入的DynamoDB实例 // const AWS = require('aws-sdk'); // const db = new AWS.DynamoDB.DocumentClient({ apiVersion: "2012-08-10" }); saveUser(userName, logger) { const Item = { id: uuid.v4(), userName, ttl: parseInt(Date.now() / 1000) + 900 // 15分钟后过期 }; // 补上Item字段 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"; import sinon from 'sinon'; import { expect } from 'chai'; import userHandler from './Handler'; const sandbox = sinon.createSandbox(); describe("user-name-handler", function() { let dbPutStub; beforeEach(() => { // Stub DocumentClient构造函数,返回带模拟put方法的对象 dbPutStub = sandbox.stub().yields(null, {}); sandbox.stub(AWS.DynamoDB, 'DocumentClient').returns({ put: dbPutStub }); }); afterEach(() => { sandbox.restore(); }); it("saveUser() 应成功保存用户并返回正确结果", async function() { const result = await userHandler.saveUser("Sample User", { log: () => {}, exception: () => {} }); // 验证put方法调用情况 expect(dbPutStub.calledOnce).to.be.true; const callArgs = dbPutStub.firstCall.args[0]; expect(callArgs.TableName).to.equal("my-table-name"); expect(callArgs.Item.userName).to.equal("Sample User"); // 验证返回结果 expect(result.statusCode).to.equal(201); expect(result.body.userName).to.equal("Sample User"); expect(result.body.ttl).to.be.a('number'); }); it("saveUser() 调用DynamoDB失败时应返回错误", async function() { // 模拟DB错误 const testError = new Error("Connection failed"); dbPutStub.yields(testError, null); try { await userHandler.saveUser("Sample User", { log: () => {}, exception: () => {} }); expect.fail("应该抛出错误"); } catch (err) { expect(err.statusCode).to.equal(404); expect(err.err).to.equal(testError); } }); });
关键说明
- 确保Stub的是handler中实际使用的DynamoDB实例,避免因实例不一致导致Stub无效(上述代码直接Stub了DocumentClient构造函数,确保handler创建的实例是模拟对象)
- 测试时不仅要验证返回结果,还要验证DynamoDB的put方法是否被正确调用,确保业务逻辑符合预期
内容的提问来源于stack exchange,提问作者deekeh
相关产品推荐
相关产品推荐

