1. 概要

自動構成がSpring Bootの主要な機能の1つであることはよく知られていますが、自動構成シナリオのテストには注意が必要です。

次のセクションでは、ApplicationContextRunnerが自動構成テストを簡素化する方法を示します。

2. 自動構成シナリオのテスト

ApplicationContextRunnerは、ApplicationContextを実行し、AssertJスタイルのアサーションを提供するユーティリティクラスです。 共有構成のテストクラスのフィールドとして使用するのが最適であり、その後、各テストでカスタマイズを行います。

private final ApplicationContextRunner contextRunner 
    = new ApplicationContextRunner();

いくつかのケースをテストして、その魔法を示すことに移りましょう。

2.1. テストクラスの条件

このセクションでは、@ConditionalOnClassおよび@ConditionalOnMissingClassアノテーションを使用するいくつかの自動構成クラスをテストします。

@Configuration
@ConditionalOnClass(ConditionalOnClassIntegrationTest.class)
protected static class ConditionalOnClassConfiguration {
    @Bean
    public String created() {
        return "This is created when ConditionalOnClassIntegrationTest "
               + "is present on the classpath";
    }
}

@Configuration
@ConditionalOnMissingClass(
    "com.baeldung.autoconfiguration.ConditionalOnClassIntegrationTest"
)
protected static class ConditionalOnMissingClassConfiguration {
    @Bean
    public String missed() {
        return "This is missed when ConditionalOnClassIntegrationTest "
               + "is present on the classpath";
    }
}

自動構成が、予想される条件で作成されたおよび欠落したBeanを適切にインスタンス化またはスキップするかどうかをテストしたいと思います。

ApplicationContextRunner は、 withUserConfiguration メソッドを提供します。このメソッドでは、テストごとにApplicationContextをカスタマイズするための自動構成をオンデマンドで提供できます。

run メソッドは、アサーションをコンテキストに適用するパラメーターとしてContextConsumerを取ります。  ApplicationContext は、テストが終了すると自動的に閉じられます。

@Test
public void whenDependentClassIsPresent_thenBeanCreated() {
    this.contextRunner.withUserConfiguration(ConditionalOnClassConfiguration.class)
        .run(context -> {
            assertThat(context).hasBean("created");
            assertThat(context.getBean("created"))
              .isEqualTo("This is created when ConditionalOnClassIntegrationTest "
                         + "is present on the classpath");
        });
}

@Test
public void whenDependentClassIsPresent_thenBeanMissing() {
    this.contextRunner.withUserConfiguration(ConditionalOnMissingClassConfiguration.class)
        .run(context -> {
            assertThat(context).doesNotHaveBean("missed");
        });
}

前の例を通して、特定のクラスがクラスパスに存在するシナリオをテストすることの単純さがわかります。 しかし、クラスパスにクラスがない場合、どのように逆をテストしますか?

これは、FilteredClassLoaderがキックインする場所です。 これは、実行時にクラスパスで指定されたクラスをフィルタリングするために使用されます。

@Test
public void whenDependentClassIsNotPresent_thenBeanMissing() {
    this.contextRunner.withUserConfiguration(ConditionalOnClassConfiguration.class)
        .withClassLoader(new FilteredClassLoader(ConditionalOnClassIntegrationTest.class))
        .run((context) -> {
            assertThat(context).doesNotHaveBean("created");
            assertThat(context).doesNotHaveBean(ConditionalOnClassIntegrationTest.class);
        });
}

@Test
public void whenDependentClassIsNotPresent_thenBeanCreated() {
    this.contextRunner.withUserConfiguration(ConditionalOnMissingClassConfiguration.class)
        .withClassLoader(new FilteredClassLoader(ConditionalOnClassIntegrationTest.class))
        .run((context) -> {
            assertThat(context).hasBean("missed");
            assertThat(context).getBean("missed")
              .isEqualTo("This is missed when ConditionalOnClassIntegrationTest "
                         + "is present on the classpath");
            assertThat(context).doesNotHaveBean(ConditionalOnClassIntegrationTest.class);
        });
}

2.2. Beanの状態をテストする

テストを見たところです @ConditionalOnClass @ConditionalOnMissingClass 注釈、今 @ConditionalOnBeanおよび@ConditionalOnMissingBeanアノテーションを使用しているときの様子を見てみましょう。

開始するには、同様にいくつかの自動構成クラスが必要です。

@Configuration
protected static class BasicConfiguration {
    @Bean
    public String created() {
        return "This is always created";
    }
}
@Configuration
@ConditionalOnBean(name = "created")
protected static class ConditionalOnBeanConfiguration {
    @Bean
    public String createOnBean() {
        return "This is created when bean (name=created) is present";
    }
}
@Configuration
@ConditionalOnMissingBean(name = "created")
protected static class ConditionalOnMissingBeanConfiguration {
    @Bean
    public String createOnMissingBean() {
        return "This is created when bean (name=created) is missing";
    }
}

次に、前のセクションのように withUserConfiguration メソッドを呼び出し、カスタム構成クラスを送信して、自動構成がcreateOnBeanまたはcreateOnMissingBean[を適切にインスタンス化またはスキップするかどうかをテストします。 X246X]さまざまな条件の豆

@Test
public void whenDependentBeanIsPresent_thenConditionalBeanCreated() {
    this.contextRunner.withUserConfiguration(
        BasicConfiguration.class, 
        ConditionalOnBeanConfiguration.class
    )
    // ommitted for brevity
}
@Test
public void whenDependentBeanIsNotPresent_thenConditionalMissingBeanCreated() {
    this.contextRunner.withUserConfiguration(ConditionalOnMissingBeanConfiguration.class)
    // ommitted for brevity
}

2.3. テストプロパティの状態

このセクションでは、@ConditionalOnPropertyアノテーションを使用する自動構成クラスをテストしてみましょう。

まず、このテスト用のプロパティが必要です。

com.baeldung.service=custom

その後、ネストされた自動構成クラスを記述して、前述のプロパティに基づいてBeanを作成します。

@Configuration
@TestPropertySource("classpath:ConditionalOnPropertyTest.properties")
protected static class SimpleServiceConfiguration {
    @Bean
    @ConditionalOnProperty(name = "com.baeldung.service", havingValue = "default")
    @ConditionalOnMissingBean
    public DefaultService defaultService() {
        return new DefaultService();
    }
    @Bean
    @ConditionalOnProperty(name = "com.baeldung.service", havingValue = "custom")
    @ConditionalOnMissingBean
    public CustomService customService() {
        return new CustomService();
    }
}

ここで、 withPropertyValues メソッドを呼び出して、各テストのプロパティ値をオーバーライドします。

@Test
public void whenGivenCustomPropertyValue_thenCustomServiceCreated() {
    this.contextRunner.withPropertyValues("com.baeldung.service=custom")
        .withUserConfiguration(SimpleServiceConfiguration.class)
        .run(context -> {
            assertThat(context).hasBean("customService");
            SimpleService simpleService = context.getBean(CustomService.class);
            assertThat(simpleService.serve()).isEqualTo("Custom Service");
            assertThat(context).doesNotHaveBean("defaultService");
        });
}

@Test
public void whenGivenDefaultPropertyValue_thenDefaultServiceCreated() {
    this.contextRunner.withPropertyValues("com.baeldung.service=default")
        .withUserConfiguration(SimpleServiceConfiguration.class)
        .run(context -> {
            assertThat(context).hasBean("defaultService");
            SimpleService simpleService = context.getBean(DefaultService.class);
            assertThat(simpleService.serve()).isEqualTo("Default Service");
            assertThat(context).doesNotHaveBean("customService");
        });
}

3. 結論

要約すると、このチュートリアルでは、 ApplicationContextRunnerを使用して、カスタマイズを使用してApplicationContextを実行し、アサーションを適用する方法を示しました。

ここでは、ApplicationContextをカスタマイズする方法の完全なリストではなく、最も頻繁に使用されるシナリオについて説明しました。

それまでの間、 ApplicationConetxtRunner は非Webアプリケーション用であることに注意してください。したがって、サーブレットベースのWebアプリケーションには WebApplicationContextRunner を、リアクティブWebにはReactiveWebApplicationContextRunnerを検討してください。アプリケーション。

このチュートリアルのソースコードは、GitHubにあります。