Testing @Scheduled in spring
JavaSpringUnit TestingSchedulerJava Problem Overview
Spring offers the possibility to schedule and execute tasks at specific intervals using annotations, e.g. @Scheduled
Is there a convenient way to unit test this behavior?
Of course I could call the method of the bean myself, but I want to make sure I don't run into problems like multiple executions due to misconfiguration and so on.
Other frameworks offer the possibility to fast forward the time yourself. One example is Activiti where you can call
org.activiti.engine.impl.util.ClockUtil.setCurrentTime(date)
to fast forward the time used by the framework.
Is there something comparable in Spring?
Essentially what I want to do is something like this in a unit test (run using SpringJUnit4ClassRunner
)
@Test public void testTaskScheduling() {
assertThat(someOtherBean.getSomeProperty(), is(equalTo(whatIinitiallyExpect)));
SpringClockUtil.setDate(dateInTwoHours)// This is what I am missing
SpringTaskExecutor.executeAllScheduledTasks() // Also missing
assertThat(someOtherBean.getSomeProperty(), is(equalTo(whatIexpectNow)));
}
Java Solutions
Solution 1 - Java
You can test the actual method execution using the regular JUnit, but to test if the @Scheduled(cron = "0 * * * * *")
you specified is correct you can use:
@Test
public void testScheduler(){
// to test if a cron expression runs only from Monday to Friday
org.springframework.scheduling.support.CronTrigger trigger =
new CronTrigger("0 0 1 * * MON-FRI");
Calendar today = Calendar.getInstance();
today.set(Calendar.DAY_OF_WEEK, Calendar.FRIDAY);
SimpleDateFormat df = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss EEEE");
final Date yesterday = today.getTime();
log.info("Yesterday was : " + df.format(yesterday));
Date nextExecutionTime = trigger.nextExecutionTime(
new TriggerContext() {
@Override
public Date lastScheduledExecutionTime() {
return yesterday;
}
@Override
public Date lastActualExecutionTime() {
return yesterday;
}
@Override
public Date lastCompletionTime() {
return yesterday;
}
});
String message = "Next Execution date: " + df.format(nextExecutionTime);
log.info(message);
}
Here is the output:
Yesterday was : 2015/11/06 11:41:58 Friday
Next Execution date: 2015/11/09 01:00:00 Monday
As the last execution (set in the TriggerContext
) was a Friday, the next execution will be on the following Monday.
I was fiddling with the Spring api and I found this solution, I hope this helps somebody as it helped me.
Solution 2 - Java
Test the scheduled code by invoking the bean directly.
Then test the scheduling configuration by:
- deploying your code in a test environment, letting it run for a while and inspecting logs and/or results (assuming the scheduled code does some logging and/or produces visible results) afterwards.
or
- externalizing the scheduling configuration in Spring XML config using the
<task: />
namespace and injecting a unit test-specific interval/schedule (preferably short and frequent to be usable in a unit/integration test) usingPropertyPlaceHolderConfigurer
. Then in your test verify that the scheduled code (be it mocked or the real thing) was invoked the proper number of times in the given (short) amount of time.