java 在 Spring Boot IntegrationTest 上禁用 @Schedule

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/40684903/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-03 05:24:57  来源:igfitidea点击:

Disable @Schedule on Spring Boot IntegrationTest

javaspringspring-bootautomated-testsspring-scheduled

提问by fassoline

How can I disable the schedule auto-start on Spring Boot IntegrationTest?

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

Thanks.

谢谢。

回答by Duke0fAnkh

Be aware that external components could be enabling scheduling automatically (see HystrixStreamAutoConfigurationand MetricExportAutoConfigurationfrom the Spring Framework). So if you try and use @ConditionalOnPropertyor @Profileon the @Configurationclass that specifies @EnableScheduling, then scheduling will be enabled anyway due to external components.

请注意,外部组件可能会自动启用调度(请参阅Spring 框架中的HystrixStreamAutoConfigurationMetricExportAutoConfiguration)。因此,如果您尝试使用@ConditionalOnProperty或指定@Profile@Configuration@EnableScheduling,那么由于外部组件,无论如何都会启用调度。

One solution

一种解决方案

Have one @Configurationclass that enables scheduling via @EnableScheduling, but then have your scheduled jobs in separate classes, each of those using @ConditionalOnPropertyto enable/disable the classes that contain the @Scheduled tasks.

拥有一个@Configuration通过 启用调度的类,@EnableScheduling然后将调度的作业放在单独的类中,每个类都@ConditionalOnProperty用于启用/禁用包含@Scheduled 任务的类。

Don't have the @Scheduledand @EnableSchedulingin the same class, or you will have the issue where external components are enabling it anyway, so the @ConditionalOnPropertyis ignored.

不要将@Scheduled@EnableScheduling放在同一个类中,否则您将遇到外部组件启用它的问题,因此@ConditionalOnProperty忽略 。

Eg:

例如:

@Configuration
@EnableScheduling
public 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 @ConditionalOnPropertyspecified. If you miss that annotation, then the job will run.

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

Another Solution

另一种解决方案

Extend the ThreadPoolTaskSchedulerand override the TaskSchedulermethods. In these methods you can perform a check to see if the job should run.

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

Then, in your @Configuration class where you use @EnableScheduling, you also create a @Bean called taskScheduler which returns your custom thread pool task scheduler).

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

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

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

@Configuration
@EnableScheduling
public 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.

上面的潜在问题是您已经创建了对内部 Spring 类的依赖,因此如果将来有更改,您必须修复兼容性。

回答by MandeSin

I had the same problem. Tried Spring's @ConditionalOnPropertyattribute with my Scheduling Bean but Scheduling still got activated in tests.

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

The only good workaround I found was to overwrite the scheduling properties in the Test class, so that the job does not have a realchance to run.

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

If your real job runs every 5 minutes using property my.cron=0 0/5 * * * *

如果您的实际工作使用属性每 5 分钟运行一次 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.

因此,即使您的作业被激活,它也只会在 2 月 29 日的第 0 小时运行,这种情况每 4 年发生一次。所以你运行它的机会非常渺茫。

You can come up with more fancy cron settings to suit your requirements.

您可以想出更多花哨的 cron 设置来满足您的要求。

回答by Bunarro

An easy solution I figured out in Spring Boot 2.0.3:

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

1) extract scheduled method(s) to a separate bean

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

@Service
public 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

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

@RunWith(SpringRunner.class)
@SpringBootTest
public class SomeTaskServiceIT {

  @Autowired
  private SomeTaskService someTaskService;

  @MockBean
  private SchedulerService schedulerService;
}

回答by Samuel Morais

One way is to use Spring profiles

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

In your test class:

在您的测试课程中:

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

In your scheduler class or method:

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

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

    ...
}

回答by Tom

When your real Spring Boot Application class looks like this:

当您真正的 Spring Boot Application 类如下所示时:

@SpringBootApplication   
@EnableScheduling
public class MyApplication {

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

}

you would have to create another Application class without @EnableScheduling for your integration tests like this:

您必须为您的集成测试创建另一个没有 @EnableScheduling 的 Application 类,如下所示:

@SpringBootApplication   
public class MyTestApplication {

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

}

And then use the MyTestApplication class in your integration test like this

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

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

...
}

That's the way I do it, since I have not found a better way.

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

回答by Tobske

I solved this problem by using a separate configuration class and then overwrite this class in the test context. So instead of putting the annotation at the application I only put it at the separate configuration classes.
Normal context:

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

@Configuration
@EnableScheduling 
public class SpringConfiguration {}

Test context:

测试上下文:

@Configuration
public class SpringConfiguration {}

回答by Felix Aballi

Integrating some answers from above:

整合上面的一些答案:

  • Create a separate configuration class for testing purposes (a.k.a "TestConfiguration.class")
  • Enable Mockito annotations there for other beans: schedulers, etc. - Read this: 2)

    @ConditionalOnClass
    @ConditionalOnMissingBean
    @ConditionalOnBean
    @ConditionalOnJava
    @ConditionalOnJndi
    @ConditionalOnMissingClass
    @ConditionalOnExpression
    @ConditionalOnNotWebApplication
    @ConditionalOnWebApplication
    @ConditionalOnProperty
    @ConditionalOnResource
    @ConditionalOnSingleCandidate
    
  • 为测试目的创建一个单独的配置类(又名“TestConfiguration.class”)
  • 为其他 bean 启用 Mockito 注释:调度程序等。 - Read this: 2)

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

Plus, always checking:

另外,始终检查:

  • "application.yml" self-creation properties depending on external devices/services
  • Auto-configuration annotations on Main class breaking beans initialization sequence
  • "applicationContext.xml", "beans.xml" or Classpath loaders
  • “application.yml”自创属性取决于外部设备/服务
  • Main 类中断 bean 初始化序列上的自动配置注释
  • “applicationContext.xml”、“beans.xml”或类路径加载器

Please, read these:

请阅读这些: