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

如何对DynamoDB增强客户端事务请求进行单元测试?

问题:DynamoDB事务请求Builder在单元测试中触发NPE

业务代码片段

public CompletableFuture<Void> createNewUser(User user) {

     TransactPutItemEnhancedRequest<Profile> profileRequest = TransactPutItemEnhancedRequest.builder(Profile.class)
            .conditionExpression(Expression.builder()
                    .expression("attribute_not_exists(profileId)")
                    .build())
            .item(profile)
            .build();

    TransactPutItemEnhancedRequest<User> userRequest = TransactPutItemEnhancedRequest.builder(User.class)
            .conditionExpression(Expression.builder()
                    .expression("attribute_not_exists(userId)")
                    .build())
            .item(user)
            .build();

    TransactWriteItemsEnhancedRequest request = TransactWriteItemsEnhancedRequest.builder()
            .addPutItem(usersTableViaUser, userRequest)                
            .addPutItem(profilesTable, profileRequest)
            .build();  // 此处抛出NPE

    // 后续逻辑...
}

注入的表依赖

DynamoDbAsyncTable<Profile> profilesTable,
DynamoDbAsyncTable<User> usersTableViaUser,

问题核心

执行TransactWriteItemsEnhancedRequest.builder().build()时,Builder会在后台调用传入的DynamoDbAsyncTable对象的tableSchema()、tableName()、mapperExtension()等方法,而单元测试中Mock的表对象未预设这些方法的返回值,导致空指针异常。

这种设计不合理——参数/表的处理逻辑应该推迟到实际调用DynamoDB服务时执行,而非构建请求阶段。相关源码片段:

// TransactWriteItemsEnhancedRequest Class
private <T> TransactWriteItem generateTransactWriteItem(MappedTableResource<T> mappedTableResource, TransactableWriteOperation<T> generator) {
      return generator.generateTransactWriteItem(mappedTableResource.tableSchema(), DefaultOperationContext.create(mappedTableResource.tableName()), mappedTableResource.mapperExtension());
}

无需手动Mock所有字段的解决方法

1. 使用AWS SDK官方测试工具类

AWS SDK for Java提供了专门的测试桩实现,通过DynamoDbEnhancedAsyncClient的模拟构建器生成的表实例会自动填充必要字段,避免NPE:

// 创建模拟增强客户端
DynamoDbEnhancedAsyncClient mockEnhancedClient = DynamoDbEnhancedAsyncClient.builder().mock();
// 生成测试用表实例
DynamoDbAsyncTable<User> mockUsersTable = mockEnhancedClient.table("users-table", TableSchema.fromBean(User.class));
DynamoDbAsyncTable<Profile> mockProfilesTable = mockEnhancedClient.table("profiles-table", TableSchema.fromBean(Profile.class));

2. 封装请求构建逻辑并Mock封装类

将事务请求的构建逻辑抽离到单独的封装类中,单元测试时直接Mock这个封装类,跳过实际的Builder构建流程,直接返回预设的请求对象。

3. 配置Mockito默认返回策略

如果必须使用Mockito,可配置Mock对象的默认返回策略为自动生成深层Stub,仅手动Stub核心必要方法:

// 开启深Mock,自动生成嵌套对象的Stub
DynamoDbAsyncTable<User> mockUsersTable = Mockito.mock(DynamoDbAsyncTable.class, Mockito.RETURNS_DEEP_STUBS);
// 仅Stub核心必填方法
Mockito.when(mockUsersTable.tableSchema()).thenReturn(TableSchema.fromBean(User.class));
Mockito.when(mockUsersTable.tableName()).thenReturn("users-table");

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.18 19:05:36