1. 概要

以前の@ConfigurationProperties ガイドでは、@ConfigurationPropertiesアノテーションをSpring Bootで設定および使用して外部構成を操作する方法を学習しました。

このチュートリアルでは、 @ConfigurationPropertiesアノテーションに依存する構成クラスをテストして、構成データがロードされ、対応するフィールドに正しくバインドされていることを確認する方法について説明します。

2. 依存関係

Mavenプロジェクトでは、 spring-boot-starterspring-boot-starter-test の依存関係を使用して、コアspringAPIとSpringのテストAPI。 さらに、 spring-boot-starter-validationをbean検証依存関係として使用します。

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.5.0</version>
</parent>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-validation</artifactId>
</dependency>

3. ユーザー定義のPOJOにバインドするプロパティ

外部化された構成で作業する場合、通常、一致する構成プロパティに対応するフィールドを含むPOJOを作成します。 すでに知っているように、Springは構成プロパティを作成したJavaクラスに自動的にバインドします。

まず、 src / test / resources /server-config-test.propertiesというプロパティファイル内にサーバー構成があると仮定します。

server.address.ip=192.168.0.1
server.resources_path.imgs=/root/imgs

前のプロパティファイルに対応する単純な構成クラスを定義します。

@Configuration
@ConfigurationProperties(prefix = "server")
public class ServerConfig {

    private Address address;
    private Map<String, String> resourcesPath;

    // getters and setters
}

また、対応するアドレスタイプ:

public class Address {

    private String ip;

    // getters and setters
}

最後に、 ServerConfig POJOをテストクラスに挿入し、そのすべてのフィールドが正しく設定されていることを検証します。

@ExtendWith(SpringExtension.class)
@EnableConfigurationProperties(value = ServerConfig.class)
@TestPropertySource("classpath:server-config-test.properties")
public class BindingPropertiesToUserDefinedPOJOUnitTest {

    @Autowired
    private ServerConfig serverConfig;

    @Test
    void givenUserDefinedPOJO_whenBindingPropertiesFile_thenAllFieldsAreSet() {
        assertEquals("192.168.0.1", serverConfig.getAddress().getIp());

        Map<String, String> expectedResourcesPath = new HashMap<>();
        expectedResourcesPath.put("imgs", "/root/imgs");
        assertEquals(expectedResourcesPath, serverConfig.getResourcesPath());
    }
}

このテストでは、次の注釈を使用しました。

  • @ExtendWith –SpringのTestContextフレームワークをJUnit5と統合します
  • @EnableConfigurationProperties @ConfigurationProperties Bean(この場合、 ServerConfig Bean)のサポートを有効にします
  • @TestPropertySource –デフォルトのapplication.propertiesファイルをオーバーライドするテストファイルを指定します

4. @ ConfigurationProperties on @Beanメソッド

構成Beanを作成する別の方法は、@Beanメソッドの@ConfigurationPropertiesアノテーションを使用することです

たとえば、次の getDefaultConfigs()メソッドは、ServerConfig構成Beanを作成します。

@Configuration
public class ServerConfigFactory {

    @Bean(name = "default_bean")
    @ConfigurationProperties(prefix = "server.default")
    public ServerConfig getDefaultConfigs() {
        return new ServerConfig();
    }
}

ご覧のとおり、を編集しなくても、 getDefaultConfigs()メソッドで@ConfigurationPropertiesを使用してServerConfigインスタンスを構成できます。 ServerConfigクラス自体。 これは、アクセスが制限されている外部のサードパーティクラスを操作する場合に特に役立ちます。

次に、サンプルの外部プロパティを定義します。

server.default.address.ip=192.168.0.2

最後に、 ApplicationContextをロードするときにServerConfigFactoryクラスを使用するようにSpringに指示するために(したがって、構成beanを作成します)、@ContextConfiguration[X194Xを追加します。 ]テストクラスへの注釈:

@ExtendWith(SpringExtension.class)
@EnableConfigurationProperties(value = ServerConfig.class)
@ContextConfiguration(classes = ServerConfigFactory.class)
@TestPropertySource("classpath:server-config-test.properties")
public class BindingPropertiesToBeanMethodsUnitTest {

    @Autowired
    @Qualifier("default_bean")
    private ServerConfig serverConfig;
    
    @Test
    void givenBeanAnnotatedMethod_whenBindingProperties_thenAllFieldsAreSet() {
        assertEquals("192.168.0.2", serverConfig.getAddress().getIp());

        // other assertions...
    }
}

5. プロパティの検証

SpringBootでbeanvalidate を有効にするには、トップレベルクラスに@Validatedアノテーションを付ける必要があります。 次に、必要なjavax.validation制約を追加します。

@Configuration
@ConfigurationProperties(prefix = "validate")
@Validated
public class MailServer {

    @NotNull
    @NotEmpty
    private Map<String, @NotBlank String> propertiesMap;

    @Valid
    private MailConfig mailConfig = new MailConfig();

    // getters and setters
}

同様に、MailConfigクラスにもいくつかの制約があります。

public class MailConfig {

    @NotBlank
    @Email
    private String address;

    // getters and setters
}

有効なデータセットを提供することにより:

validate.propertiesMap.first=prop1
validate.propertiesMap.second=prop2
validate.mail_config.address=user1@test

アプリケーションは正常に起動し、単体テストに合格します。

@ExtendWith(SpringExtension.class)
@EnableConfigurationProperties(value = MailServer.class)
@TestPropertySource("classpath:property-validation-test.properties")
public class PropertyValidationUnitTest {

    @Autowired
    private MailServer mailServer;

    private static Validator propertyValidator;

    @BeforeAll
    public static void setup() {
        propertyValidator = Validation.buildDefaultValidatorFactory().getValidator();
    }

    @Test
    void whenBindingPropertiesToValidatedBeans_thenConstrainsAreChecked() {
        assertEquals(0, propertyValidator.validate(mailServer.getPropertiesMap()).size());
        assertEquals(0, propertyValidator.validate(mailServer.getMailConfig()).size());
    }
}

逆に、無効なプロパティを使用すると、Springは起動時にIllegalStateExceptionをスローします

たとえば、次の無効な構成のいずれかを使用します。

validate.propertiesMap.second=
validate.mail_config.address=user1.test

このエラーメッセージでアプリケーションが失敗します:

Property: validate.propertiesMap[second]
Value:
Reason: must not be blank

Property: validate.mailConfig.address
Value: user1.test
Reason: must be a well-formed email address

mailConfigフィールドで@Validを使用して、validate.mailConfig.addressが定義されていない場合でも、MailConfig制約がチェックされていることを確認します。それ以外の場合、SpringはmailConfigを設定します]をnullに変更し、アプリケーションを正常に起動します。

6. プロパティ変換

Spring Bootプロパティ変換を使用すると、一部のプロパティを特定のタイプに変換できます。

このセクションでは、Springの組み込み変換を使用する構成クラスをテストすることから始めます。 次に、自分で作成するカスタムコンバーターをテストします。

6.1. Spring Bootのデフォルトの変換

次のデータサイズと期間のプロパティについて考えてみましょう。

# data sizes
convert.upload_speed=500MB
convert.download_speed=10

# durations
convert.backup_day=1d
convert.backup_hour=8

Spring Bootは、これらのプロパティを、PropertyConversion構成クラスで定義された一致するDataSizeフィールドとDurationフィールドに自動的にバインドします。

@Configuration
@ConfigurationProperties(prefix = "convert")
public class PropertyConversion {

    private DataSize uploadSpeed;

    @DataSizeUnit(DataUnit.GIGABYTES)
    private DataSize downloadSpeed;

    private Duration backupDay;

    @DurationUnit(ChronoUnit.HOURS)
    private Duration backupHour;

    // getters and setters
}

変換結果を確認します。

@ExtendWith(SpringExtension.class)
@EnableConfigurationProperties(value = PropertyConversion.class)
@ContextConfiguration(classes = CustomCredentialsConverter.class)
@TestPropertySource("classpath:spring-conversion-test.properties")
public class SpringPropertiesConversionUnitTest {

    @Autowired
    private PropertyConversion propertyConversion;

    @Test
    void whenUsingSpringDefaultSizeConversion_thenDataSizeObjectIsSet() {
        assertEquals(DataSize.ofMegabytes(500), propertyConversion.getUploadSpeed());
        assertEquals(DataSize.ofGigabytes(10), propertyConversion.getDownloadSpeed());
    }

    @Test
    void whenUsingSpringDefaultDurationConversion_thenDurationObjectIsSet() {
        assertEquals(Duration.ofDays(1), propertyConversion.getBackupDay());
        assertEquals(Duration.ofHours(8), propertyConversion.getBackupHour());
    }
}

6.2. カスタムコンバーター

ここで、convert.credentialsプロパティを変換するとします。

convert.credentials=user,123

次のCredentialクラスに:

public class Credentials {

    private String username;
    private String password;

    // getters and setters
}

これを実現するために、カスタムコンバーターを実装できます。

@Component
@ConfigurationPropertiesBinding
public class CustomCredentialsConverter implements Converter<String, Credentials> {

    @Override
    public Credentials convert(String source) {
        String[] data = source.split(",");
        return new Credentials(data[0], data[1]);
    }
}

最後に、CredentialsフィールドをPropertyConversionクラスに追加します。

public class PropertyConversion {
    private Credentials credentials;
    // ...
}

Spring PropertiesConversionUnitTest テストクラスでは、 @ContextConfiguration を追加して、Springのコンテキストにカスタムコンバーターを登録する必要があります。

// other annotations
@ContextConfiguration(classes=CustomCredentialsConverter.class)
public class SpringPropertiesConversionUnitTest {
    
    //...
    
    @Test
    void whenRegisteringCustomCredentialsConverter_thenCredentialsAreParsed() {
        assertEquals("user", propertyConversion.getCredentials().getUsername());
        assertEquals("123", propertyConversion.getCredentials().getPassword());
    }
}

前のアサーションが示すように、 Springはカスタムコンバーターを使用して、convert.credentialsプロパティをCredentialsインスタンスに解析しました。

7. YAMLドキュメントのバインド

階層構成データの場合、YAML構成の方が便利な場合があります。 YAMLは、同じドキュメント内での複数のプロファイルの定義もサポートしています。

src / test / resources/の下にある次のapplication.ymlは、ServerConfigクラスの「テスト」プロファイルを定義します。

spring:
  config:
    activate:
      on-profile: test
server:
  address:
    ip: 192.168.0.4
  resources_path:
    imgs: /etc/test/imgs
---
# other profiles

その結果、次のテストに合格します。

@ExtendWith(SpringExtension.class)
@ContextConfiguration(initializers = ConfigDataApplicationContextInitializer.class)
@EnableConfigurationProperties(value = ServerConfig.class)
@ActiveProfiles("test")
public class BindingYMLPropertiesUnitTest {

    @Autowired
    private ServerConfig serverConfig;

    @Test
    void whenBindingYMLConfigFile_thenAllFieldsAreSet() {
        assertEquals("192.168.0.4", serverConfig.getAddress().getIp());

        // other assertions ...
    }
}

使用した注釈に関するいくつかの注意事項:

  • @ContextConfiguration(initializers = ConfigDataApplicationContextInitializer.cla ss)application.ymlファイルをロードします
  • @ActiveProfiles( “test”) –このテスト中に「テスト」プロファイルが使用されることを指定します

最後に、@ProperySourceも@TestProperySourceも.ymlファイルの読み込みをサポートしていないことに注意してくださいしたがって、YAML構成は常にapplication.ymlファイル内に配置する必要があります。

8. @ConfigurationProperties構成のオーバーライド

特にテストの場合、 @ConfigurationPropertiesによってロードされた構成プロパティを別のデータセットでオーバーライドしたい場合があります。

前の例で見たように、 @TestPropertySource(“ path_to_new_data_set”)を使用して、元の構成全体( / src / main / resourcesの下)を新しい構成に置き換えることができます。

または、@ TestPropertySource のプロパティ属性を使用して、元のプロパティの一部を選択的に置き換えることもできます。

以前に定義したvalidate.mail_config.addressプロパティを別の値でオーバーライドするとします。 テストクラスに@TestPropertySource、で注釈を付け、プロパティリストを介して同じプロパティに新しい値を割り当てるだけです。

@TestPropertySource(properties = {"validate.mail_config.address=new_user@test"})

したがって、Springは新しく定義された値を使用します。

assertEquals("new_user@test", mailServer.getMailConfig().getAddress());

9. 結論

この記事では、@ConfigurationPropertiesアノテーションを使用して.propertiesおよび.yml構成ファイルをロードするさまざまなタイプの構成クラスをテストする方法を学習しました。

いつものように、この記事のソースコードはGitHubから入手できます。