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

如何测试Quarkus定时调度任务,验证其在指定时间运行

Quarkus定时任务测试方案(针对柏林时间3点执行的清理任务)

问题场景

我在Java Quarkus应用中编写了如下类与数据清理方法,该方法通过Quarkus定时调度注解配置为每天柏林时间3点执行:

@ApplicationScoped
public class DeprecatedDataRemovalBean {
    private static final Logger LOG = Logger.getLogger(DeprecatedDataRemovalBean.class);

    @Inject
    JobService jobService;

    //at 3 o'clock in the morning
    @Scheduled(cron = "0 0 3 * * ?", timeZone = "Europe/Berlin")
    void removeDeprecatedData(ScheduledExecution execution) {
        LOG.info("Starting the data removal at " + execution.getScheduledFireTime());
        try {
            jobService.removeDataIfReadCountIsMoreThanOne();
            LOG.info("Data removal process completed successfully.");
        } catch (Exception e) {
            LOG.error("An error occurred during the data removal process: ", e);
        }
    }
}

希望编写测试用例,将系统时间设置为3点,验证该定时任务确实会在此时运行,但之前尝试的测试均未成功,求可行方案。

可行测试方案

Quarkus提供了专门的定时任务测试支持,结合时间模拟和依赖注入Mock,可以完成验证,具体步骤如下:

1. 添加必要依赖

确保pom.xml(Maven)或build.gradle(Gradle)中包含Quarkus测试相关依赖:

<!-- Maven -->
<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-junit5</artifactId>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-core</artifactId>
    <scope>test</scope>
</dependency>

2. 编写测试类

使用@QuarkusTest启动测试容器,通过@InjectMock替代真实的JobService,并利用Quarkus的调度器时间模拟功能控制系统时间:

import io.quarkus.test.junit.QuarkusTest;
import io.quarkus.test.junit.mockito.InjectMock;
import io.quarkus.scheduler.Scheduler;
import org.junit.jupiter.api.Test;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.Instant;
import static org.mockito.Mockito.verify;

@QuarkusTest
public class DeprecatedDataRemovalBeanTest {

    @InjectMock
    JobService jobService;

    @Inject
    Scheduler scheduler;

    @Test
    void testScheduledDataRemovalAtBerlin3AM() throws InterruptedException {
        // 构造柏林时间凌晨3点的时间点
        ZonedDateTime berlin3AM = ZonedDateTime.now(ZoneId.of("Europe/Berlin"))
                .withHour(3)
                .withMinute(0)
                .withSecond(0)
                .withNano(0);
        Instant targetTime = berlin3AM.toInstant();

        // 开启调度器的时间模拟并设置目标时间
        scheduler.mockTime().setTime(targetTime);

        // 触发调度器立即检查待执行任务
        scheduler.evaluateJobs();

        // 短暂等待任务执行完成
        Thread.sleep(1000);

        // 验证JobService的清理方法是否被调用
        verify(jobService).removeDataIfReadCountIsMoreThanOne();
    }
}

3. 关键说明

  • 时间模拟:Quarkus调度器原生支持mockTime()方法,无需额外扩展即可模拟系统时间,精准匹配定时任务的时区配置。
  • Mock依赖:用@InjectMock替代真实业务服务,避免测试时操作生产数据,同时方便验证方法调用情况。
  • 主动触发检查:调用scheduler.evaluateJobs()让调度器立即扫描任务,无需等待自然时间流逝到3点。
  • 时区对齐:明确使用Europe/Berlin时区构造目标时间,确保和定时任务的时区配置完全一致,避免时区差导致的触发失败。

备选方案:直接测试业务逻辑

如果仅需验证清理方法的业务正确性,无需验证调度器的触发逻辑,可以直接调用目标方法:

import io.quarkus.test.junit.QuarkusTest;
import io.quarkus.test.junit.mockito.InjectMock;
import org.junit.jupiter.api.Test;
import static org.mockito.Mockito.verify;

@QuarkusTest
public class DeprecatedDataRemovalBeanDirectTest {

    @Inject
    DeprecatedDataRemovalBean removalBean;

    @InjectMock
    JobService jobService;

    @Test
    void testDataRemovalLogic() {
        // 直接调用清理方法,传入null或模拟ScheduledExecution对象
        removalBean.removeDeprecatedData(null);
        // 验证业务方法是否被执行
        verify(jobService).removeDataIfReadCountIsMoreThanOne();
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 18:52:33