1. 개요

우리의 이전 사용방법(예제)에서는 @ConfigurationProperties , 우리는 설정하고 사용하는 방법을 배웠습니다 @ConfigurationProperties의 외부 구성 작업을위한 Spring 부팅과 어노테이션을.

이 예제에서는 구성 데이터가 해당 필드에 올바르게로드되고 바인딩되는지 확인하기 위해 @ConfigurationProperties 어노테이션 에 의존하는 구성 클래스를 테스트하는 방법을 보여줍니다 .

2. 의존성

Maven 프로젝트에서는 spring-boot-starterspring-boot-starter-test 의존성을 사용하여 각각 핵심 스프링 API와 Spring의 테스트 API를 활성화합니다.

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.4.1</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>

또한 나중에 사용할 것이므로 빈 유효성 검사 의존성으로 프로젝트를 구성 해 보겠습니다 .

<!-- JSR-380 bean validation -->
<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-validator</artifactId>
</dependency>
<dependency>
    <groupId>javax.el</groupId>
    <artifactId>javax.el-api</artifactId>
    <version>3.0.0</version>
</dependency>
<dependency>
    <groupId>org.glassfish.web</groupId>
    <artifactId>javax.el</artifactId>
    <version>2.2.6</version>
</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@Bean 메소드

구성 빈을 생성하는 또 다른 방법은 @Bean 메소드 @ConfigurationProperties 어노테이션을 사용하는 것 입니다.

예를 들어, 다음 getDefaultConfigs () 메소드는 ServerConfig 구성 Bean을 작성합니다 .

@Configuration
public class ServerConfigFactory {

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

보시다시피 , ServerConfig 클래스 자체 를 편집하지 않고도 getDefaultConfigs () 메서드에서 @ConfigurationProperties사용하여 ServerConfig 인스턴스 를 구성 할 수 있습니다. 이는 액세스가 제한된 외부 타사 클래스로 작업 할 때 특히 유용 할 수 있습니다.

다음으로 샘플 외부 속성을 정의 해 보겠습니다.

server.default.address.ip=192.168.0.2

마지막으로 Spring에 ApplicationContext를 로드 할 때 ServerConfigFactory 클래스 를 사용하도록 지시하기 위해 (따라서 구성 빈을 생성) 테스트 클래스에 @ContextConfiguration 어노테이션을 추가합니다 .

@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. 속성 유효성 검사

Spring Boot에서 빈 유효성 검사 를 활성화하려면 최상위 클래스에 @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은 start-up에서 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

통지 우리가 사용했던 @Valid를mailConfig의 수 있도록 필드 MailConfig의 경우에도 제약이 체크 validate.mailConfig.address가 정의되지 않았습니다. 그렇지 않으면 Spring은 mailConfignull로 설정 하고 응용 프로그램을 정상적으로 시작합니다.

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 구성 클래스에 정의 된 일치하는 DataSizeDuration 필드에 자동으로 바인딩합니다 .

@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]);
    }
}

마지막으로 PropertyConversion 클래스에 Credentials 필드를 추가해 보겠습니다 .

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

우리에 SpringPropertiesConversionUnitTest 테스트 클래스, 우리는 또한 추가 할 필요가 @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.ymlServerConfig 클래스에 대한 "테스트"프로필을 정의합니다 .

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") –이 테스트 중에 "test"프로필이 사용되도록 지정합니다.

마지막으로 @ProperySource@TestProperySource.yml 파일 로드를 지원 하지 않는다는 점에 유의 하세요 . 따라서 항상 application.yml 파일 내에 YAML 구성을 배치해야 합니다 .

8. @ConfigurationProperties 구성 재정의

때로는 특히 테스트 할 때 @ConfigurationProperties의해로드 된 구성 속성 을 다른 데이터 세트 재정의 하고 싶을 수 있습니다 .

이전 예제에서 살펴본 것처럼 @TestPropertySource ( "path_to_new_data_set") 를 사용하여 전체 원래 구성 ( / src / main / resources 아래) 을 새 구성으로 바꿀 수 있습니다 .

또는 @TestPropertySource속성 속성을 사용하여 원래 속성 중 일부를 선택적으로 대체 할 수도 있습니다 .

이전에 정의 된 validate.mail_config.address 속성을 다른 값 으로 재정의한다고 가정 합니다. 우리가해야 할 일은 @TestPropertySource로 테스트 클래스에 어노테이션을 달고 속성 List을 통해 동일한 속성에 새 값을 할당하는 것입니다 .

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

결과적으로 Spring은 새로 정의 된 값을 사용합니다.

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

9. 결론

이 사용방법(예제)에서는 @ConfigurationProperties 어노테이션을 사용하여 .properties.yml 구성 파일 을로드 하는 다양한 유형의 구성 클래스를 테스트하는 방법을 살펴 보았습니다 .

평소처럼이 문서의 소스 코드는 GitHub에서 사용할 수 있습니다 .