如何在Spring Boot IntegrationTest上禁用计划自动启动?
谢谢。
请注意,外部组件可能会自动启用调度(请参阅Spring Framework中的HystrixStreamAutoConfiguration和MetricExportAutoConfiguration)。因此,如果您尝试在指定@ConditionalOnProperty
的@Profile
类上使用@Configuration
或@EnableScheduling
,则无论如何都会因外部组件而启用调度。
有一个@Configuration
类可以通过@EnableScheduling
进行调度,但是然后将你的预定作业放在不同的类中,每个类使用@ConditionalOnProperty
来启用/禁用包含@Scheduled任务的类。
不要在同一个类中使用@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
并覆盖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类的依赖,所以如果将来有变化,你必须修复兼容性。
我有同样的问题。尝试使用我的调度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 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 {
...
}
这就是我这样做的方式,因为我没有找到更好的方法。
我在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;
}
一种方法是使用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 {}
整合上面的一些答案:
- Read this: 2)
@ConditionalOnClass
@ConditionalOnMissingBean
@ConditionalOnBean
@ConditionalOnJava
@ConditionalOnJndi
@ConditionalOnMissingClass
@ConditionalOnExpression
@ConditionalOnNotWebApplication
@ConditionalOnWebApplication
@ConditionalOnProperty
@ConditionalOnResource
@ConditionalOnSingleCandidate
另外,请务必检查:
请阅读以下内容:
@MockBean
private StoresRatingAvgScheduler scheduler;
@Before
public void init() {
MockitoAnnotations.initMocks(this);
}