Как я могу протестировать @Scheduled
задания @Scheduled
в моем приложении весенней загрузки?
package com.myco.tasks;
public class MyTask {
@Scheduled(fixedRate=1000)
public void work() {
// task execution logic
}
}
Как я могу протестировать @Scheduled
задания @Scheduled
в моем приложении весенней загрузки?
package com.myco.tasks;
public class MyTask {
@Scheduled(fixedRate=1000)
public void work() {
// task execution logic
}
}
Если мы предположим, что ваша работа выполняется с такими небольшими интервалами, что вы действительно хотите, чтобы ваш тест дождался выполнения задания, и вы просто хотите проверить, запущено ли задание, вы можете использовать следующее решение:
Добавить 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());
}
}
Мой вопрос: "что вы хотите проверить?"
Если вы ответите "Я хочу знать, что Spring выполняет мое запланированное задание, когда я этого хочу", то вы тестируете Spring, а не ваш код. Это не то, что вам нужно для модульного тестирования.
Если вы ответите "Я хочу знать, что я правильно настроил свою задачу", то напишите тестовое приложение с часто выполняемой задачей и убедитесь, что задача выполняется, когда вы ожидаете, что она будет запущена. Это не unit тест, но покажет, что вы знаете, как правильно настроить текст.
Если ответ "Я хочу знать, что задание, которое я написал, работает правильно", то вам нужно выполнить модульное тестирование метода задачи. В вашем примере вы хотите выполнить модульное тестирование метода work()
. Сделайте это, написав unit тест, который напрямую вызывает ваш метод задачи (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.
}
Это часто бывает трудно. Вы можете рассмотреть возможность загрузки контекста Spring во время теста и подделать его с помощью bean, чтобы иметь возможность проверять запланированный вызов.
У меня есть такой пример в моем реестре Github. Простой пример запланированного примера с описанным подходом.
этот класс означает создание планировщиков 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
Мы можем использовать как минимум два подхода для тестирования запланированных задач с помощью Spring:
Если мы используем весеннюю загрузку, нам понадобятся следующие зависимости:
<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>
Мы могли бы добавить count
к Task
и увеличить его внутри метода 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();
}
}
Затем проверьте 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);
}
}
В этом случае нам нужно добавить зависимость Awaitility:
<dependency>
<groupId>org.awaitility</groupId>
<artifactId>awaitility</artifactId>
<version>3.1.6</version>
<scope>test</scope>
</dependency>
И используйте его 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());
}
}
Мы должны принять во внимание, что, хотя они хороши, лучше сосредоточиться на модульном тестировании логики внутри метода работы.
Я положил пример здесь.
Решение, упомянутое Maciej Walkowiak, отлично работает, но с небольшими изменениями. @SpyBean не работал для меня, поэтому я использовал @autowired, и он работал отлично.