首页 > 解决方案 > 使用 cron 进行单元测试 @Sheduled

问题描述

我需要测试是否在每个午夜执行以下计划方法:

public class CacheService {

  /** Evicts token from the cache at midnight */
  @Scheduled(cron = "0 0 0 * * *", zone = "Europe/London")
  @CacheEvict(value = CacheConfig.AUTH_TOKEN, allEntries = true)
  public void clearCacheAtMidnight() {
    log.info("Scheduled cache clean at: " + Instant.now());
  }
}

我正在使用以下单元测试:

@SpringBootTest
class CacheServiceTest {

  @SpyBean private CacheService cacheService;

  @Test
  void shouldClearCacheAtMidnight() {
    Instant.now(Clock.fixed(Instant.parse("2020-01-01T00:00:00Z"), ZoneId.of("Europe/London")));
    await()
            .atMost(Duration.ofMinutes(1))
            .untilAsserted(() -> verify(cacheService, atLeast(1)).clearCacheAtMidnight());
  }
}

但不是快乐。如果我从 cron 更改为 fixrate,它会完美运行。我有一种感觉,我没有正确覆盖系统时钟,或者我没有正确使用设置的间隔。

有什么线索吗?

标签: javaspring-boot

解决方案


Spring 内部使用CronSequenceGenerator解析 cron 表达式以找出下一个触发时间。如果要测试是否正确设置了 cron 表达式,可以参考他们的测试用例并编写自己的测试用例,例如:

    @Test
    public void myTest() {

        CronSequenceGenerator cronSequenceGenerator = new CronSequenceGenerator("0 0 0 * * *",TimeZone.getTimeZone("Europe/London"));
        ZonedDateTime date = LocalDateTime.of(2020, 6, 1, 9, 52, 0).atZone(ZoneId.of("Europe/London"));
        ZonedDateTime expected = LocalDateTime.of(2020, 6, 2, 0, 0, 0).atZone(ZoneId.of("Europe/London"));
        assertThat(cronSequenceGenerator.next(Date.from(date.toInstant()))).isEqualTo(Date.from(expected.toInstant()));

    }

我同意一些测试人员在测试框架时不会建议这样做,但我肯定会包含它,因为它很容易编写,至少我会更有信心我的 cron 表达式配置正确,尤其是在某些时候我们需要设置一些重要的 cron 表达式。


推荐阅读