使用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返回一个带有promiseStub的对象,才能正确模拟异步调用逻辑。 - 参数严格匹配:Sinon的
withArgs()是严格匹配参数的,要确保测试中定义的params和业务代码中传入的参数完全一致(包括空格、键名大小写等)。
如果还是遇到问题,可以检查是否在测试启动前(比如app初始化时)提前调用了getDynamoSingleton,如果有,需要确保Stub逻辑在这个调用之前执行。
内容的提问来源于stack exchange,提问作者juan dubie
相关产品推荐
相关产品推荐

