在Spring Boot IntegrationTest上禁用@Schedule

如何在Spring Boot IntegrationTest上禁用计划自动启动?

谢谢。

请注意,外部组件可能会自动启用调度(请参阅Spring Framework中的HystrixStreamAutoConfiguration和MetricExportAutoConfiguration )。 因此,如果您尝试在指定@EnableScheduling@Configuration类上使用@ConditionalOnProperty@Profile ,则无论如何都会由于外部组件而启用调度。

一个解决方案

有一个@Configuration类可以通过@EnableScheduling启用调度,但是然后将您的预定作业放在单独的类中,每个类使用@ConditionalOnProperty来启用/禁用包含@Scheduled任务的类。

不要在同一个类中使用@Scheduled@EnableScheduling ,否则无论如何都会遇到外部组件启用它的问题,因此忽略@ConditionalOnProperty

例如:

 @Configuration @EnableScheduling public class MyApplicationSchedulingConfiguration { } 

然后在另一个class级

 @Named @ConditionalOnProperty(value = "scheduling.enabled", havingValue = "true", matchIfMissing = false) public class MyApplicationScheduledTasks { @Scheduled(fixedRate = 60 * 60 * 1000) public void runSomeTaskHourly() { doStuff(); } } 

此解决方案的问题是每个预定作业都需要在其自己的类中指定@ConditionalOnProperty 。 如果您错过了该注释,那么该作业将会运行。

另一个方案

扩展ThreadPoolTaskScheduler并覆盖TaskScheduler方法。 在这些方法中,您可以执行检查以查看作业是否应该运行。

然后,在使用@EnableScheduling的@Configuration类中,还创建一个名为taskScheduler的@Bean,它返回自定义线程池任务调度程序。

例如:

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

使用我们的自定义调度程序创建taskScheduler bean的配置类,并启用调度

 @Configuration @EnableScheduling public class MyApplicationSchedulingConfiguration { @Bean public TaskScheduler taskScheduler() { return new ConditionalThreadPoolTaskScheduler(); } } 

上面的潜在问题是你已经创建了对内部Spring类的依赖,所以如果将来有变化,你必须修复兼容性。

当你真正的Spring Boot Application类看起来像这样:

 @SpringBootApplication @EnableScheduling public class MyApplication { public static void main(String[] args) { SpringApplication.run(MyApplication.class, args); } } 

您将不得不为没有@EnableScheduling的另一个Application类创建这样的集成测试:

 @SpringBootApplication public class MyTestApplication { public static void main(String[] args) { SpringApplication.run(MyTestApplication.class, args); } } 

然后在集成测试中使用MyTestApplication类

 RunWith(SpringJUnit4ClassRunner.class) @SpringBootTest(classes = MyTestApplication.class) public class MyIntegrationTest { ... } 

这就是我这样做的方式,因为我没有找到更好的方法。

我有同样的问题。 使用我的调度Bean尝试了Spring的@ConditionalOnProperty属性,但调度仍然在测试中被激活。

我发现的唯一好的解决方法是覆盖Test类中的调度属性,这样作业就没有真正的运行机会。

如果您的真实工作每5分钟运行一次,使用属性my.cron=0 0/5 * * * *

 public class MyJob { @Scheduled(cron = "${my.cron}") public void execute() { // do something } } 

然后在测试类中,您可以将其配置为:

 @RunWith(SpringRunner.class) @SpringBootTest(properties = {"my.cron=0 0 0 29 2 ?"}) // Configured as 29 Feb ;-) public class MyApplicationTests { @Test public void contextLoads() { } } 

因此,即使您的工作被激活,它也只会在2月29日的第0小时运行,这将在4年内发生一次。 所以你运行它的机会很小。

您可以提供更多精美的cron设置以满足您的要求。

一种方法是使用Spring配置文件

在您的测试类中:

 @SpringBootTest(classes = Application.class) @ActiveProfiles("integration-test") public class SpringBootTestBase { ... } 

在您的调度程序类或方法中:

 @Configuration @Profile("!integration-test") //to disable all from this configuration public class SchedulerConfiguration { @Scheduled(cron = "${some.cron}") @Profile("!integration-test") //to disable a specific scheduler public void scheduler1() { // do something } @Scheduled(cron = "${some.cron}") public void scheduler2() { // do something } ... } 

我通过使用单独的配置类解决了这个问题,然后在测试上下文中覆盖了这个类。 因此,不是将注释放在应用程序中,而是将其放在单独的配置类中。
正常背景:

 @Configuration @EnableScheduling public class SpringConfiguration {} 

测试环境:

 @Configuration public class SpringConfiguration {} 

我在Spring Boot 2.0.3中找到了一个简单的解决方案:

1)将预定方法提取到单独的bean

 @Service public class SchedulerService { @Autowired private SomeTaskService someTaskService; @Scheduled(fixedRate = 60 * 60 * 1000) public void runSomeTaskHourly() { someTaskService.runTask(); } } 

2)模拟测试类中的调度程序bean

 @RunWith(SpringRunner.class) @SpringBootTest public class SomeTaskServiceIT { @Autowired private SomeTaskService someTaskService; @MockBean private SchedulerService schedulerService; }