如何使用Mockito模拟JobExecution.builder()及DynamoDBMapper进行单元测试?
用Mockito测试DynamoDB中JobExecution的保存逻辑
测试思路
你的代码里有两个重载的私有saveOrUpdateToDynamoDb方法:第一个会构建新的JobExecution实例,再调用第二个真正执行DB保存的方法。要测试这个逻辑,我们需要模拟DynamoDB的Mapper依赖,验证它的save方法是否被正确调用,且传入的参数符合预期。
因为方法是私有,要么通过触发调用它的公共方法来测试,要么用PowerMockito直接调用私有方法并验证行为。下面给出完整的测试示例。
依赖准备
确保你的测试依赖里包含Mockito和PowerMockito(用于处理私有方法和成员变量):
<!-- Maven依赖示例 --> <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-core</artifactId> <version>4.11.0</version> <scope>test</scope> </dependency> <dependency> <groupId>org.powermock</groupId> <artifactId>powermock-module-junit4</artifactId> <version>2.0.9</version> <scope>test</scope> </dependency> <dependency> <groupId>org.powermock</groupId> <artifactId>powermock-api-mockito2</artifactId> <version>2.0.9</version> <scope>test</scope> </dependency>
测试代码示例
假设你的目标类叫JobService,测试类写法如下:
import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.powermock.api.mockito.PowerMockito; import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.PowerMockRunner; import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapper; import java.time.Instant; @RunWith(PowerMockRunner.class) @PrepareForTest(JobService.class) // 标记目标类用于字节码操作 public class JobServiceTest { @Mock private DynamoDBMapper dbMapper; // 模拟DynamoDB操作依赖 @InjectMocks private JobService jobService; // 注入模拟对象到目标业务类 // 测试用的常量模拟值 private final String TEST_CAP_CODE = "TEST_CAP"; private final String TEST_ENV = "TEST"; private final String TEST_TABLE = "JOBS"; private final String TABLE_NAME_PATTERN = "%s_%s_%s"; @Test public void testSaveOrUpdateToDynamoDb() throws Exception { // 1. 准备测试数据与模拟成员变量 Instant testStartTime = Instant.now(); String testPayload = "test-message-body"; // 给目标类的私有成员变量赋值 PowerMockito.field(JobService.class, "CAPABILITY_CODE").set(jobService, TEST_CAP_CODE); PowerMockito.field(JobService.class, "envName").set(jobService, TEST_ENV); PowerMockito.field(JobService.class, "TABLE_NAME").set(jobService, TEST_TABLE); PowerMockito.field(JobService.class, "STRING_CONCAT").set(jobService, TABLE_NAME_PATTERN); PowerMockito.field(JobService.class, "startTime").set(jobService, testStartTime); PowerMockito.field(JobService.class, "messageBody").set(jobService, testPayload); // 2. 触发私有方法(如果有公共方法调用该私有方法,优先调用公共方法更贴合真实场景) PowerMockito.invokePrivate(jobService, "saveOrUpdateToDynamoDb", (JobExecution) null); // 3. 验证DB保存行为是否符合预期 long expectedTtl = 5259600 + Instant.now().getEpochSecond(); String expectedTableName = String.format(TABLE_NAME_PATTERN, TEST_CAP_CODE, TEST_ENV, TEST_TABLE); // 验证dbMapper.save的参数是否正确 PowerMockito.verifyPrivate(dbMapper).invoke("save", org.mockito.ArgumentMatchers.argThat(jobExec -> "12".equals(jobExec.getJobMasterId()) && "lamda".equals(jobExec.getJobType()) && "Entry message load into MC".equals(jobExec.getJobAction()) && "running".equals(jobExec.getStatus()) && testStartTime.equals(jobExec.getStartDate()) && "225".equals(jobExec.getReferenceId()) && testPayload.equals(jobExec.getPayload()) && Math.abs(jobExec.getTtlExpiry() - expectedTtl) < 2 // 允许少量时间误差 ), org.mockito.ArgumentMatchers.argThat(config -> config.getTableNameOverride().getTableName().equals(expectedTableName) ) ); } }
关键说明
- 私有方法触发:用
PowerMockito.invokePrivate直接调用私有方法;如果该私有方法由公共方法触发,优先调用公共方法,测试场景更真实。 - 参数校验:通过
argThat匹配复杂对象,逐一验证JobExecution的属性是否符合构建逻辑,同时校验动态生成的表名是否正确。 - 时间处理:TTL是实时计算的值,验证时允许1-2秒的误差,避免因系统时间差导致测试失败。
内容的提问来源于stack exchange,提问作者Dhushy_A
相关产品推荐
相关产品推荐

