如何使用Jest正确mock AWS SDK v3的DynamoDBClient实现Node.js Lambda单元测试
AWS SDK v3 DynamoDBClient 的 Jest 正确 Mock 方案
原测试代码失效的核心原因
原代码未通过 Jest 的 jest.mock() 机制接管整个 @aws-sdk/client-dynamodb 模块,直接对导入的 DynamoDBClient 类调用 mockImplementation 无实际效果——此时导入的对象还是 SDK 导出的真实客户端类,并非 Jest 生成的可模拟对象,最终实际实例化真实客户端发起网络请求,触发线上资源不存在的报错。
正确实现步骤
- 在测试文件最顶部声明模块Mock:将
jest.mock写在所有require/import语句之前(Jest 会自动提升 Mock 声明的执行顺序,确保先完成模块模拟再加载业务代码),把DynamoDBClient替换为可模拟的 Jest 函数,实例上的send方法替换为可自定义的 Jest Mock 函数。 - 在测试用例中定制Mock返回逻辑:针对每个测试场景,给
send方法设置对应返回值,同时通过 Mock 函数的断言能力,验证客户端是否收到了正确的调用参数。 - 在每个测试前重置Mock状态:避免上一个测试场景设置的返回值、调用记录影响后续测试用例的执行结果。
完整可运行的测试代码示例
// 测试文件最开头,所有引入语句之前完成模块Mock // 定义独立的send mock函数,方便测试中直接调用和断言 const mockDynamoSend = jest.fn() jest.mock('@aws-sdk/client-dynamodb', () => ({ DynamoDBClient: jest.fn().mockImplementation(() => ({ send: mockDynamoSend })) })) const LambdaTester = require('lambda-tester') const handler = require('./my-handler').handler describe('handler', function () { // 每个测试前清空所有mock的调用记录和自定义实现,避免交叉污染 beforeEach(() => { jest.clearAllMocks() }) it('Handler should work properly', async function () { // 准备DynamoDB返回的模拟响应 const mockSuccessResponse = { Item: { a: { S: 'a' }, b: { S: 'b' } } } // 给send方法设置Promise形式的返回值,匹配真实SDK的异步行为 mockDynamoSend.mockResolvedValue(mockSuccessResponse) await LambdaTester(handler) .event({ // 填入你的测试事件参数 }) .expectResult((res) => { // 业务结果断言 // 同时验证DynamoDB客户端被正确调用 expect(mockDynamoSend).toHaveBeenCalledWith( expect.objectContaining({ input: expect.objectContaining({ TableName: '你的目标DynamoDB表名' }) }) ) }) }) })
常见注意事项
- 如果项目中还用到了
@aws-sdk/lib-dynamodb(兼容旧版SDK的DocumentClient能力),按照完全相同的逻辑对该模块做Mock即可,核心思路是对所有引入的AWS SDK模块提前声明Mock,不直接使用真实模块的类。 - 不要将
jest.mock写在测试用例内部,否则会因为执行顺序问题导致Mock不生效,最终依然调用真实的SDK方法。 - 上述写法兼容两种客户端实例化场景:无论Handler是在模块加载时全局实例化DynamoDBClient,还是在每次Lambda调用时临时实例化,Mock逻辑都会正常生效。
内容的提问来源于stack exchange,提问作者Maksim Danilau
相关产品推荐
相关产品推荐

