Spring Boot IntegrationTestでスケジュールの自動起動を無効にするにはどうすればよいですか?
ありがとう。
外部コンポーネントがスケジューリングを自動的に有効にしている可能性があることに注意してください(Spring Frameworkの HystrixStreamAutoConfiguration および MetricExportAutoConfiguration を参照)。したがって、@ConditionalOnProperty
を指定する@Profile
クラスで@Configuration
または@EnableScheduling
を使用しようとすると、外部コンポーネントが原因でスケジューリングが有効になります。
@Configuration
を介したスケジューリングを有効にする@EnableScheduling
クラスが1つありますが、それぞれのスケジュールされたジョブは@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クラスへの依存関係を作成したことです。したがって、将来変更があった場合は、互換性を修正する必要があります。
同じ問題がありました。 Scheduling BeanでSpringの@ConditionalOnProperty
属性を試しましたが、テストでSchedulingがまだアクティブになっています。
私が見つけた唯一の良い回避策は、Testクラスのスケジューリングプロパティを上書きして、ジョブにrealが実行される機会がないようにすることでした。
プロパティmy.cron=0 0/5 * * * *
を使用して実際のジョブが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() {
}
}
そのため、ジョブがアクティブ化されていても、4年に1回発生する2月29日の0時間にのみ実行されます。したがって、実行する可能性は非常にわずかです。
要件に合わせて、さらに派手なcron設定を考え出すことができます。
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
}
...
}
実際の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;
}
別の構成クラスを使用してこの問題を解決し、テストコンテキストでこのクラスを上書きしました。そのため、アプリケーションに注釈を配置する代わりに、別個の構成クラスにのみ配置します。
通常のコンテキスト:
@Configuration
@EnableScheduling
public class SpringConfiguration {}
テストコンテキスト:
@Configuration
public class SpringConfiguration {}
上記のいくつかの答えを統合する:
他の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);
}