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

如何使用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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.30 13:21:28