web-dev-qa-db-ja.com

Spring Boot IntegrationTestで@Scheduleを無効にする

Spring Boot IntegrationTestでスケジュールの自動起動を無効にするにはどうすればよいですか?

ありがとう。

15
assoline

外部コンポーネントがスケジューリングを自動的に有効にしている可能性があることに注意してください(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クラスへの依存関係を作成したことです。したがって、将来変更があった場合は、互換性を修正する必要があります。

19
Duke0fAnkh

同じ問題がありました。 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設定を考え出すことができます。

5
MandeSin

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
    }

    ...
}
2
Samuel Morais

実際の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 {

...
}

私はより良い方法を見つけていないので、それは私がそれを行う方法です。

1
Tom

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;
}
1
Bunarro

別の構成クラスを使用してこの問題を解決し、テストコンテキストでこのクラスを上書きしました。そのため、アプリケーションに注釈を配置する代わりに、別個の構成クラスにのみ配置します。
通常のコンテキスト:

@Configuration
@EnableScheduling 
public class SpringConfiguration {}

テストコンテキスト:

@Configuration
public class SpringConfiguration {}
0
Tobske

上記のいくつかの答えを統合する:

  • テスト用に別の構成クラスを作成します(別名「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」、またはクラスパスローダー

これらを読んでください:

0
Felix Aballi