如何在Spring Boot IntegrationTest上禁用计划自动启动?
感谢。
答案 0 :(得分:17)
请注意,外部组件可能会自动启用调度(请参阅Spring Framework中的HystrixStreamAutoConfiguration和MetricExportAutoConfiguration)。因此,如果您尝试在指定@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)
从上面综合一些答案:
在那里为其他bean(调度程序等)启用Mockito注释。- Read this: 2)
@ConditionalOnClass
@ConditionalOnMissingBean
@ConditionalOnBean
@ConditionalOnJava
@ConditionalOnJndi
@ConditionalOnMissingClass
@ConditionalOnExpression
@ConditionalOnNotWebApplication
@ConditionalOnWebApplication
@ConditionalOnProperty
@ConditionalOnResource
@ConditionalOnSingleCandidate
此外,请务必检查:
请阅读以下内容:
尝试在不需要的bean上使用模拟注释进行测试,例如:
@MockBean
private StoresRatingAvgScheduler scheduler;
@Before
public void init() {
MockitoAnnotations.initMocks(this);
}