Disable @Schedule on Spring Boot IntegrationTest
Be aware that external components could be enabling scheduling automatically (see HystrixStreamAutoConfiguration and MetricExportAutoConfiguration from the Spring Framework). So if you try and use @ConditionalOnProperty
or @Profile
on the @Configuration
class that specifies @EnableScheduling
, then scheduling will be enabled anyway due to external components.
One solution
Have one @Configuration
class that enables scheduling via @EnableScheduling
, but then have your scheduled jobs in separate classes, each of those using @ConditionalOnProperty
to enable/disable the classes that contain the @Scheduled tasks.
Don't have the @Scheduled
and @EnableScheduling
in the same class, or you will have the issue where external components are enabling it anyway, so the @ConditionalOnProperty
is ignored.
Eg:
@Configuration@EnableSchedulingpublic class MyApplicationSchedulingConfiguration {}
and then in a separate class
@Named@ConditionalOnProperty(value = "scheduling.enabled", havingValue = "true", matchIfMissing = false)public class MyApplicationScheduledTasks { @Scheduled(fixedRate = 60 * 60 * 1000) public void runSomeTaskHourly() { doStuff(); }}
The issue with this solution is that every scheduled job needs to be in it's own class with @ConditionalOnProperty
specified. If you miss that annotation, then the job will run.
Another Solution
Extend the ThreadPoolTaskScheduler
and override the TaskScheduler
methods. In these methods you can perform a check to see if the job should run.
Then, in your @Configuration class where you use @EnableScheduling, you also create a @Bean called taskScheduler which returns your custom thread pool task scheduler).
Eg:
public class ConditionalThreadPoolTaskScheduler extends ThreadPoolTaskScheduler { @Inject private Environment environment; // Override the TaskScheduler methods @Override public ScheduledFuture<?> schedule(Runnable task, Trigger trigger) { if (!canRun()) { return null; } return super.schedule(task, trigger); } @Override public ScheduledFuture<?> schedule(Runnable task, Date startTime) { if (!canRun()) { return null; } return super.schedule(task, startTime); } @Override public ScheduledFuture<?> scheduleAtFixedRate(Runnable task, Date startTime, long period) { if (!canRun()) { return null; } return super.scheduleAtFixedRate(task, startTime, period); } @Override public ScheduledFuture<?> scheduleAtFixedRate(Runnable task, long period) { if (!canRun()) { return null; } return super.scheduleAtFixedRate(task, period); } @Override public ScheduledFuture<?> scheduleWithFixedDelay(Runnable task, Date startTime, long delay) { if (!canRun()) { return null; } return super.scheduleWithFixedDelay(task, startTime, delay); } @Override public ScheduledFuture<?> scheduleWithFixedDelay(Runnable task, long delay) { if (!canRun()) { return null; } return super.scheduleWithFixedDelay(task, delay); } private boolean canRun() { if (environment == null) { return false; } if (!Boolean.valueOf(environment.getProperty("scheduling.enabled"))) { return false; } return true; }}
Configuration class that creates the taskScheduler bean using our custom scheduler, and enables scheduling
@Configuration@EnableSchedulingpublic class MyApplicationSchedulingConfiguration { @Bean public TaskScheduler taskScheduler() { return new ConditionalThreadPoolTaskScheduler(); }}
The potential issue with the above is that you've created a dependency on an internal Spring class, so if there are changes in the future, you'd have to fix compatibility.
I had the same problem. Tried Spring's @ConditionalOnProperty
attribute with my Scheduling Bean but Scheduling still got activated in tests.
The only good workaround I found was to overwrite the scheduling properties in the Test class, so that the job does not have a real chance to run.
If your real job runs every 5 minutes using property my.cron=0 0/5 * * * *
public class MyJob { @Scheduled(cron = "${my.cron}") public void execute() { // do something }}
Then in the test class you can configure it as:
@RunWith(SpringRunner.class)@SpringBootTest(properties = {"my.cron=0 0 0 29 2 ?"}) // Configured as 29 Feb ;-)public class MyApplicationTests { @Test public void contextLoads() { }}
So even if your job is activated, it will only run at the 0th hour of 29 Feb which happens once in 4 years. So you have a very slim chance of running it.
You can come up with more fancy cron settings to suit your requirements.
An easy solution I figured out in Spring Boot 2.0.3:
1) extract scheduled method(s) to a separate bean
@Servicepublic class SchedulerService { @Autowired private SomeTaskService someTaskService; @Scheduled(fixedRate = 60 * 60 * 1000) public void runSomeTaskHourly() { someTaskService.runTask(); }}
2) mock the scheduler bean in your test class
@RunWith(SpringRunner.class)@SpringBootTestpublic class SomeTaskServiceIT { @Autowired private SomeTaskService someTaskService; @MockBean private SchedulerService schedulerService;}