テスト方法 @Scheduled
私のスプリングブートアプリケーションのジョブタスクは?
package com.myco.tasks;
public class MyTask {
@Scheduled(fixedRate=1000)
public void work() {
// task execution logic
}
}
ジョブが実行されるのをテストで待ちたいほど短い間隔でジョブが実行されると仮定し、ジョブが呼び出されたかどうかをテストしたい場合は、次のソリューションを使用できます。
Awaitility をクラスパスに追加します:
<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をテストしています。これは、単体テストに必要なものではありません。
答えが「タスクを正しく構成したことを知りたい」の場合は、頻繁に実行するタスクを使用してテストアプリを作成し、実行する予定のときにタスクが実行されることを確認します。これは単体テストではありませんが、テキストを正しく構成する方法を知っていることを示します。
答えが「私が書いたタスクが正しく機能することを知りたい」の場合、タスクメソッドをユニットテストする必要があります。この例では、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.
}
これはしばしば困難です。テスト中に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でスケジュールされたタスクをテストするには、少なくとも2つのアプローチを使用できます。
スプリングブートを使用する場合、次の依存関係が必要になります。
<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());
}
}
良いが、作業メソッド内のロジックの単体テストに焦点を当てる方が良いことを考慮する必要があります。
例を挙げます here 。