如何测试Spring @Scheduled

S P*_*din 17 java junit spring mockito spring-boot

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

 package com.myco.tasks;

 public class MyTask {
     @Scheduled(fixedRate=1000)
     public void work() {
         // task execution logic
     }
 }
Run Code Online (Sandbox Code Playgroud)

Mac*_*iak 21

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

Awaitility添加到类路径:

<dependency>
    <groupId>org.awaitility</groupId>
    <artifactId>awaitility</artifactId>
    <version>3.1.0</version>
    <scope>test</scope>
</dependency>
Run Code Online (Sandbox Code Playgroud)

写测试类似于:

@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());
    }
}
Run Code Online (Sandbox Code Playgroud)

  • @CristianBatista,您可以通过使用属性而不是对其进行硬编码,在测试中对 cron 作业使用不同的频率。 (2认同)

DwB*_*DwB 21

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

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

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

如果答案是“我想知道我编写的任务是否正确运行”,那么您需要对任务方法进行单元测试。在您的示例中,您希望对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.
  }
Run Code Online (Sandbox Code Playgroud)


Mif*_*eet 5

@Maciej的回答解决了问题,但没有解决 @cristian-batista 提到的以太长的间隔(例如小时)测试 @Scheduled 的困难部分。

为了独立于实际的调度间隔来测试@Scheduled,我们需要使其可以通过测试进行参数化。幸运的是,Spring为此添加了一个fixedRateString参数。

这是一个完整的示例:

public class MyTask {
     // Control rate with property `task.work.rate` and use 3600000 (1 hour) as a default:
     @Scheduled(fixedRateString = "${task.work.rate:3600000}")
     public void work() {
         // task execution logic
     }
 }
Run Code Online (Sandbox Code Playgroud)

等待测试:

@RunWith(SpringRunner.class)
@SpringBootTest
// Override the scheduling rate to something really short:
@TestPropertySource(properties = "task.work.rate=100") 
public class DemoApplicationTests {

    @SpyBean
    private MyTask myTask;

    @Test
    public void jobRuns() {
        Awaitility.await().atMost(10, TimeUnit.SECONDS).untilAsserted(() ->
            verify(myTask, Mockito.atLeastOnce()).work()
        );
    }
}
Run Code Online (Sandbox Code Playgroud)


lub*_*nac 3

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

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

  • 仅仅等待计划任务肯定不是办法。应该是一个玩弄时钟的技巧,以便调度程序可以对其做出响应。 (6认同)
  • @rohit,请随意发布您的解决方案。如果你没有,我猜你没有。 (3认同)

归档时间:

查看次数:

22947 次

最近记录:

6 年,10 月 前