Issue
I have a scheduled task in a spring boot application:
@Scheduled(fixedRateString = "${scheduled.task.rate}")
public void runScheduledTask() {
// ...
}
With a corresponding test:
@Test
public void test_scheduledTask_runs() {
await().atMost(Duration.ofMillis(scheduledTaskRate).multipliedBy(2)).untilAsserted(() -> {
Mockito.verify(scheduledTasks, Mockito.atLeastOnce()).runScheduledTask();
});
}
Now I want to use a cron instead of a fixed rate:
@Scheduled(cron = "${scheduled.task.cron}")
Now I need to adapt the test to this. How do get a Duration
object corresponding to the frequency of the cron expression?
Solution
Spring specific solution:
Spring provides a CronExpression
class which can be used to parse a cron expression and get the next Temporal
instance at which it will be triggered after the provided Temporal
.
So to get a Duration
:
CronExpression cronExpression = CronExpression.parse(scheduledTaskCron);
LocalDateTime nextExecution = cronExpression.next(LocalDateTime.now());
LocalDateTime nextToNextExecution = cronExpression.next(nextExecution);
Duration durationBetweenExecutions = Duration.between(
nextExecution, nextToNextExecution
);
Note that Instant
can't be used as the Temporal
here, as it can't represent all the components of a cron expression, such as Day of Week
, giving the following exception:
java.time.temporal.UnsupportedTemporalTypeException: Unsupported field: DayOfWeek
at java.base/java.time.Instant.get(Instant.java:565)
at org.springframework.scheduling.support.CronField$Type.get(CronField.java:200)
at org.springframework.scheduling.support.BitsCronField.nextOrSame(BitsCronField.java:180)
at org.springframework.scheduling.support.CronExpression.nextOrSameInternal(CronExpression.java:264)
at org.springframework.scheduling.support.CronExpression.nextOrSame(CronExpression.java:252)
at org.springframework.scheduling.support.CronExpression.next(CronExpression.java:245)
Original answer for spring versions < 5.3
Spring provides a CronSequenceGenerator
which can be used to parse a cron expression and get the next Date
instance at which it will be triggered after the provided Date
.
So to get a Duration
:
CronSequenceGenerator generator = new CronSequenceGenerator(scheduledTaskCron);
Date nextExecution = generator.next(new Date());
Date nextToNextExecution = generator.next(nextExecution);
Duration durationBetweenExecutions = Duration.between(
nextExecution.toInstant(), nextToNextExecution.toInstant()
);
Answered By - aksh1618
Answer Checked By - Willingham (JavaFixing Volunteer)