Disable @Schedule on Spring Boot IntegrationTest Disable @Schedule on Spring Boot IntegrationTest spring spring

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;}