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

使用Sinon和Chai Stub AWS DynamoDB DocumentClient集成测试遇阻求助

我看了你遇到的问题,核心在于单例模式的DynamoDB客户端和Sinon stub的时机/方式没匹配上,咱们一步步来解决:

问题分析

  • 你的getDynamoSingleton是单例实现,一旦初始化就会缓存docClient,如果测试启动前(或第一个测试执行前)这个单例已经被调用过,后续的stub就不会生效,因为代码已经复用了真实的DocumentClient实例。
  • 你当前对queryMock的stub写法有问题:returns({ promise:() =>sinon.fake.resolves(...) })没有正确模拟Promise的链式调用逻辑,Sinon有更直观的方式处理异步方法的Stub。

解决方案步骤

1. 给单例添加重置方法(方便测试)

首先修改你的getDynamoSingleton代码,新增一个重置单例缓存的方法,这样每个测试前都能清空旧实例,确保Stub后的客户端能被正确初始化:

const AWS = require('aws-sdk');
AWS.config.update({region:'REGION'});
let docClient = false;

const getDynamoSingleton = async () => {
  if (docClient) return docClient;
  docClient = new AWS.DynamoDB.DocumentClient();
  console.log(docClient)
  return docClient
}

// 新增重置方法,仅用于测试场景
getDynamoSingleton.reset = () => {
  docClient = false;
}

module.exports = getDynamoSingleton;

2. 调整测试中的Stub逻辑

接下来修改测试代码,确保Stub时机和逻辑正确:

const chai = require('chai');
const sinon = require('sinon');
const chaiHttp = require('chai-http');
const app= require('./app');
const getDynamoSingleton = require('./path-to-your-singleton-file'); // 替换成你的单例文件实际路径

chai.use(chaiHttp);
const expect = chai.expect;

describe.only('Integration test for activation',()=>{
  let dynamoDBSpy;
  let queryStub;

  beforeEach(() => {
    // 1. 先重置单例缓存,确保每次测试都用新的Stub实例
    getDynamoSingleton.reset();
    
    // 2. 先Stub query方法的promise返回逻辑
    queryStub = sinon.stub().returns({
      promise: sinon.stub().resolves() // 默认空Promise,后续测试按需覆盖返回值
    });
    
    // 3. Stub DocumentClient构造函数,返回带有query Stub的对象
    const documentClientStub = sinon.stub().returns({
      query: queryStub
    });
    
    // 4. 最终Stub AWS.DynamoDB,让它返回带有DocumentClient Stub的对象
    dynamoDBSpy = sinon.stub(require('aws-sdk'), 'DynamoDB');
    dynamoDBSpy.returns({
      DocumentClient: documentClientStub
    });
  })

  afterEach(() => {
    // 恢复所有Stub,避免影响其他测试
    dynamoDBSpy.restore();
  })

  it('Request /endpoint returns HTTP 200 with {} when user exist and all task are done', async () => {
    const params = {
      TableName:'table',
      KeyConditionExpression: `client_id= :i`,
      ExpressionAttributeValues: { ':i': '23424234' },
      ConsistentRead:true
    };

    const userWithNoPendingsMock = {
      Items: [ { client_id: "23424234", } ],
      Count: 1,
      ScannedCount: 1,
    }

    // 配置当传入指定params时,query的promise返回mock数据
    queryStub.withArgs(params).returns({
      promise: sinon.stub().resolves(userWithNoPendingsMock)
    });

    // 发送请求并断言(用async/await替代done,代码更简洁)
    const res = await chai.request(app).post("/endpoint");
    expect(res.status).to.equal(200);
    expect(res.body).to.deep.equal({});
  });
})

3. 关键注意点

  • 单例重置的必要性:每次测试前调用getDynamoSingleton.reset(),彻底清空缓存的docClient,确保Stub后的AWS.DynamoDB.DocumentClient能被重新初始化。
  • 链式Stub的正确性:DocumentClient.query()返回的对象包含promise()方法,所以我们需要让queryStub返回一个带有promise Stub的对象,才能正确模拟异步调用逻辑。
  • 参数严格匹配:Sinon的withArgs()是严格匹配参数的,要确保测试中定义的params和业务代码中传入的参数完全一致(包括空格、键名大小写等)。

如果还是遇到问题,可以检查是否在测试启动前(比如app初始化时)提前调用了getDynamoSingleton,如果有,需要确保Stub逻辑在这个调用之前执行。

内容的提问来源于stack exchange,提问作者juan dubie

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.07 21:42:41