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

使用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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.23 01:42:20