Java 如何测试 Spring @Scheduled

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/32319640/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 12:20:43  来源:igfitidea点击:

How to test Spring @Scheduled

javaspringjunitspring-bootmockito

提问by S Puddin

How do I test @Scheduledjob tasks in my spring-boot application?

如何@Scheduled在 spring-boot 应用程序中测试作业任务?

 package com.myco.tasks;

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

回答by luboskrnac

This is often hard. You may consider to load Spring context during the test and fake some bean from it to be able to verify scheduled invocation.

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

I have such example in my Github repo.There is simple scheduled example tested with described approach.

我的 Github 仓库中有这样的例子。有使用所述方法测试的简单预定示例。

回答by Maciej Walkowiak

If we assume that your job runs in such a small intervals that you really want your test to wait for job to be executed and you just want to test if job is invoked you can use following solution:

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

Add Awaitilityto classpath:

等待添加到类路径:

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

Write test similar to:

编写类似于以下内容的测试:

@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());
    }
}

回答by Tiago Medici

this class stands for generating schedulers cron using springframework scheduling

此类代表使用 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));
        }

    }
}

here is the output logging:

这是输出日志记录:

<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

回答by DwB

My question is: "what do you want to test?"

我的问题是:“你想测试什么?”

If your answer is "I want to know that Spring runs my scheduled task when I want it to", then you are testing Spring, not your code. This is not something you need to unit test.

如果您的回答是“我想知道 Spring 在我想要的时候运行我的计划任务”,那么您正在测试 Spring,而不是您的代码。这不是您需要进行单元测试的内容。

If your answer is "I want to know that I configured my task correctly", then write a test app with a frequently running task and verify that the task runs when you expect it to run. This is not a unit test, but will show that you know how to configure your text correctly.

如果您的回答是“我想知道我是否正确配置了我的任务”,那么编写一个带有频繁运行任务的测试应用程序,并验证该任务是否在您期望它运行时运行。这不是单元测试,但会表明您知道如何正确配置文本。

If the answer is "I want to know that the task I wrote functions correctly", then you need to unit test the task method. In your example, you want to unit test the work()method. Do this by writing a unit test that directly calls your task method (work()). For example,

如果答案是“我想知道我编写的任务是否正确运行”,那么您需要对任务方法进行单元测试。在您的示例中,您希望对work()方法进行单元测试。通过编写一个直接调用您的任务方法 ( work())的单元测试来做到这一点。例如,

public class TestMyTask
{
  @InjectMocks
  private MyTask classToTest;

  // Declare any mocks you need.
  @Mock
  private Blammy mockBlammy;

  @Before
  public void preTestSetup()
  {
    MockitoAnnotations.initMocks(this);

    ... any other setup you need.
  }

  @Test
  public void work_success()
  {
    ... setup for the test.


    classToTest.work();


    .. asserts to verify that the work method functioned correctly.
  }

回答by SHoko

We can use at least two approaches in order to test scheduled tasks with Spring:

我们至少可以使用两种方法来使用 Spring 测试计划任务:

  • Integration testing
  • 集成测试

If we use spring boot we gonna need the following dependencies:

如果我们使用 spring boot,我们将需要以下依赖项:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter</artifactId>
</dependency>

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>

<dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter-api</artifactId>
</dependency>

We could add a countto the Taskand increment it inside the workmethod:

我们可以添加countTask和增加它的内部work方法:

 public class MyTask {
   private final AtomicInteger count = new AtomicInteger(0);

   @Scheduled(fixedRate=1000)
   public void work(){
     this.count.incrementAndGet();
   }

   public int getInvocationCount() {
    return this.count.get();
   }
 }

Then check the count:

然后检查count

@SpringJUnitConfig(ScheduledConfig.class)
public class ScheduledIntegrationTest {

    @Autowired
    MyTask task;

    @Test
    public void givenSleepBy100ms_whenWork_thenInvocationCountIsGreaterThanZero() 
      throws InterruptedException {
        Thread.sleep(2000L);

        assertThat(task.getInvocationCount()).isGreaterThan(0);
    }
}
  • Another alternative is using Awaitility like mentions @maciej-walkowiak.
  • 另一种选择是使用 Awaitility,如提及@maciej-walkowiak。

In that case, we need to add the Awaitility dependency:

在这种情况下,我们需要添加 Awaitility 依赖项:

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

And use its DSL to check the number of invocations of the method work:

并使用其 DSL 来检查方法的调用次数work

@SpringJUnitConfig(ScheduledConfig.class)
public class ScheduledAwaitilityIntegrationTest {

    @SpyBean 
    MyTask task;

    @Test
    public void whenWaitOneSecond_thenWorkIsCalledAtLeastThreeTimes() {
        await()
          .atMost(Duration.FIVE_SECONDS)
          .untilAsserted(() -> verify(task, atLeast(3)).work());
    }
}

We need take in count that although they are good it's better to focus on the unit testing of the logic inside the work method.

我们需要考虑的是,尽管它们很好,但最好将重点放在工作方法内部逻辑的单元测试上

I put an example here.

在这里一个例子。