如何对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
相关产品推荐
相关产品推荐

