在Spring Boot IntegrationTest上禁用@Schedule

时间:2016-11-18 19:45:45

标签: java spring spring-boot automated-tests spring-scheduled

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

感谢。

7 个答案:

答案 0 :(得分:17)

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

一种解决方案

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

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

例如:

@ConditionalOnProperty

然后在另一个班级

@Configuration
@EnableScheduling
public class MyApplicationSchedulingConfiguration {
}

此解决方案的问题是每个预定作业都需要在其自己的类中指定@Named @ConditionalOnProperty(value = "scheduling.enabled", havingValue = "true", matchIfMissing = false) public class MyApplicationScheduledTasks { @Scheduled(fixedRate = 60 * 60 * 1000) public void runSomeTaskHourly() { doStuff(); } } 。如果您错过了该注释,那么该作业将会运行。

另一种解决方案

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

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

例如:

TaskScheduler

使用我们的自定义调度程序创建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;
  }
}

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

答案 1 :(得分:3)

我遇到了同样的问题。使用我的调度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设置以满足您的要求。

答案 2 :(得分:1)

当你真正的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 {

...
}

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

答案 3 :(得分:1)

一种方法是使用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
    }

    ...
}

答案 4 :(得分:0)

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

@Configuration
@EnableScheduling 
public class SpringConfiguration {}

测试环境:

@Configuration
public class SpringConfiguration {}

答案 5 :(得分:0)

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

答案 6 :(得分:0)

从上面综合一些答案:

  • 为测试目的创建一个单独的配置类(也称为“ TestConfiguration.class”)
  • 在那里为其他bean(调度程序等)启用Mockito注释。- Read this: 2)

    @ConditionalOnClass
    @ConditionalOnMissingBean
    @ConditionalOnBean
    @ConditionalOnJava
    @ConditionalOnJndi
    @ConditionalOnMissingClass
    @ConditionalOnExpression
    @ConditionalOnNotWebApplication
    @ConditionalOnWebApplication
    @ConditionalOnProperty
    @ConditionalOnResource
    @ConditionalOnSingleCandidate
    

此外,请务必检查:

  • “ application.yml”自我创建属性取决于外部设备/服务
  • 主类中断bean初始化序列上的自动配置注释
  • “ applicationContext.xml”,“ beans.xml”或类路径加载器

请阅读以下内容: