问题 如何测试Spring @Scheduled


如何测试spring-boot应用程序的@ Scheduled / cron作业?

 package com.myco.tasks;

 public class MyTask {
     @Scheduled(fixedRate=1000)
     public void work() {
         // task execution logic
     }
 }

1043
2017-08-31 20:41


起源

你想要准确测试什么?如果你想测试work()做它应该做的事情,你可以像任何其他bean的任何其他方法一样测试它:你创建bean的一个实例,调用方法,并测试它做了它应该做的事情做。如果你想测试Spring每秒都确实调用了这个方法,那就没有真正的意义了:Spring已经为你测试过了。 - JB Nizet
我同意你的意见,尝试测试框架的功能对我来说似乎没有必要,但我被要求。我找到了一个解决方法,通过添加一个小的日志消息并检查预期的消息是否确实记录了预期的时间范围。 - S Puddin
测试的另一个好处是如果测试失败 @EnableScheduling 注释已删除。 - C-Otto


答案:


如果我们假设您的作业运行时间很短,您确实希望测试等待作业执行,并且您只想测试作业是否被调用,则可以使用以下解决方案:

Awaitility 到classpath:

<dependency>
    <groupId>org.awaitility</groupId>
    <artifactId>awaitility</artifactId>
    <version>3.1.0</version>
    <scope>test</scope>
</dependency>

写测试类似于:

@RunWith(SpringRunner.class)
@SpringBootTest
public class DemoApplicationTests {

    @SpyBean
    private MyTask myTask;

    @Test
    public void jobRuns() {
        await().atMost(Duration.FIVE_SECONDS)
               .untilAsserted(() -> verify(myTask, times(1)).work());
    }
}

8
2018-04-19 22:13



迟到的答案,但工作完美! - Mulgard
verify() 和 times() 无法找到功能。你能指定包吗? - LiTTle
这些功能来自Mockito。包裹是: org.mockito.Mockito#verify 和类似的 times。 - Maciej Walkowiak


这通常很难。您可以考虑在测试期间加载Spring上下文并伪造一些bean以便能够验证计划的调用。

我在Github回购中有这样的例子。 使用所描述的方法测试了简单的预定示例。


2
2017-08-31 21:01



只是等待预定的任务绝对不是办法。应该是时钟播放的技巧,以便调度程序可以响应它。 - rohit
@rohit,随意发布您的解决方案。如果你不这样做,我认为你没有。 - luboskrnac


这个类代表使用springframework调度生成调度程序cron

import org.apache.log4j.Logger;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.scheduling.support.CronSequenceGenerator;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.AbstractJUnit4SpringContextTests;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;

@RunWith(SpringJUnit4ClassRunner.class)
@Configuration
@PropertySource("classpath:application.properties")
public class TrimestralReportSenderJobTest extends AbstractJUnit4SpringContextTests {

    protected Logger LOG = Logger.getLogger(getClass());

    private static final String DATE_CURRENT_2018_01_01 = "2018-01-01";
    private static final String SCHEDULER_TWO_MIN_PERIOD = "2 0/2 * * * *";
    private static final String SCHEDULER_QUARTER_SEASON_PERIOD = "0 0 20 1-7 1,4,7,10 FRI";

    @Test
    public void cronSchedulerGenerator_0() {
        cronSchedulerGenerator(SCHEDULER_QUARTER_SEASON_PERIOD, 100);
    }

    @Test
    public void cronSchedulerGenerator_1() {
        cronSchedulerGenerator(SCHEDULER_TWO_MIN_PERIOD, 200);
    }

    public void cronSchedulerGenerator(String paramScheduler, int index) {
        CronSequenceGenerator cronGen = new CronSequenceGenerator(paramScheduler);
        java.util.Date date = java.sql.Date.valueOf(DATE_CURRENT_2018_01_01);

        for (int i = 0; i < index; i++) {
            date = cronGen.next(date);
            LOG.info(new java.text.SimpleDateFormat("EEE, MMM d, yyyy 'at' hh:mm:ss a").format(date));
        }

    }
}

这是输出记录:

<com.medici.scheduler.jobs.TrimestralReportSenderJobTest> - lun, gen 1, 2018 at 12:02:02 AM
<com.medici.scheduler.jobs.TrimestralReportSenderJobTest> - lun, gen 1, 2018 at 03:02:02 AM
<com.medici.scheduler.jobs.TrimestralReportSenderJobTest> - lun, gen 1, 2018 at 06:02:02 AM
<com.medici.scheduler.jobs.TrimestralReportSenderJobTest> - lun, gen 1, 2018 at 09:02:02 AM
<com.medici.scheduler.jobs.TrimestralReportSenderJobTest> - lun, gen 1, 2018 at 12:02:02 PM

0
2017-09-04 13:22





提到的解决方案 Maciej Walkowiak 工作正常,但变化很小。 @SpyBean不能为我工作,所以我使用了@autowired,它运行得很好。


-1
2017-08-17 13:36



这篇文章似乎没有提供 质量答案 问题。请编辑您的答案,或者将其作为对推荐答案的评论发布。 - sɐunıɔןɐqɐp