Spring Boot는 spring-boot-starter-test 모듈을 통해 test를 위한 어노테이션과 유틸클래스, 테스트 라이브러리를 제공합니다.
1. Test Scope Dependencies
- spring-boot-test : test core feature
- spring-boot-test-autoconfigure : test auto-configuration
- assertj-core : 표현식을 가진 단언 라이브러리
- hamcrest : 매처 라이브러리
- junit-jupiter : junit5 핵심 모듈. (unit test 프레임워크)
- mockito-core, mockito-junit-jupiter : mocking, stubbing 프레임워크
- JsonPath : JSON을 위한 XPath 모듈
2. Testing Spring Applications
- Spring의 핵심기능인 DI는 Unit test를 더욱 쉽게 만들어줍니다.
- 기본적으로 객체 생성은 new 키워드를 사용합니다.
- 이때 객체의 의존성은 생성자의 매개변수로 주입됩니다.
- DI를 사용할 경우, Spring Framework에서는 실제 의존성 대신 mock object를 주입해줄 수 있습니다.
- Integration test를 위한 테스트 환경 구성을 돕습니다
- Integration test는 ApplicationContext를 사용하며 테스트 환경은 실제 외부 Infrastructure를 사용하지 않습니다.
- 외부 Infrastructure를 mocking 할 수 있어, Integration test의 실행과 검증이 간소화됩니다.
3. Testing Spring Boot Applications
@SpringBootTest
- Test 환경의 자동 구성을 지원합니다.
- 표준으로 사용되는 spring-test의 @ContextConfiguration을 완벽하게 대체하며, 추가적으로 유용한 기능을 제공합니다
- 테스트 시 필요한 빈들과 설정을 자동으로 구성하기 위한 ApplicationContext를 초기화합니다.
- server 실행 관련 설정이 가능합니다.
- 기본적으로 server 실행 하지 않습니다.
- webEnvironment attribute를 통해 설정이 가능합니다.
(default) MOCK
- mock 설정이 적용된 ApplicationContext를 로딩합니다.
- Embedded server는 실행되지 않습니다.
- fall back 에서 복구를 위해 사용되는 설정입니다.
- mock설정이 모듈의 부재로 인해 실패할 경우, non-web ApplicationContext를 생성합니다.
- @AutoConfigureMockMvc 혹은 @AutoConfigureWebTestClient에 의해 자동 설정됩니다.
RANDOM_PORT
- 실제 설정이 적용된 WebServerApplicationContext를 로딩합니다.
- 임의의 포트로 Embedded server가 실행됩니다.
DEFINED_PORT
- 실제 설정이 적용된 WebServerApplicationContext를 로딩합니다.
- 설정된 포트로 Embedded server가 실행됩니다.
- property 파일의 server.port로 전달됩니다. (기본값:8080)
NONE
- 아무 설정이 되지 않은 ApplicationContext를 로딩합니다.
- SpringApplication에 의해 로딩됩니다.
Detecting Test Configuration
- @*Test 애노테이션은 primary 설정클래스를 자동적으로 검색합니다.
- 테스트가 실행되는 클래스로부터 @SpringBootApplication (혹은 @SpringBootConfiguration)이 붙은 클래스를 찾습니다.
- Slicing Test를 위한 기능을 제공합니다.
- @SpringBootApplication의 exclude attribute를 사용하여 component scan 대상을 제외할 수 있습니다.
- @TestConfiguration이 붙은 테스트용 설정클래스를 사용하여 테스트 전용 설정환경을 커스터마이징할 수 있습니다.
- @TestConfiguration는 중첩클래스 형태로 작성할 수 있습니다.
- @Configuration의 중첩클래스와 다르게 primary configuration의 보충 및 추가 용도로 사용됩니다.
- ApplicationContext은 초기에 한번 로드되며 test간 ApplicationContext는 공유됩니다.
Using the Test Configuration Main Method
- @SpringBootTest는 보통 가장 먼저 primary 설정 클래스로 @SpringBootApplication을 찾게 됩니다.
- @SpringBootApplication는 SpringApplication 셋팅 및 실행하는 main 메서드를 가진 클래스에 붙은 어노테이션입니다.
- SpringApplication의 셋팅을 통해, ApplicationContext를 커스터마이징 할 수 있습니다.
- @SpringBootTest는 primary 설정 클래스의 main 메서드를 호출하지 않고 설정클래스를 읽어 ApplicationContext 설정합니다.
- useMainMethod attribute 속성을 통해 ApplicationContext 설정 방식을 설정할 수 있습니다.
ALWAYS
- main 메소드 호출을 처리 로직에 추가하여 ApplicationContext를 설정합니다.
WHEN_AVAILABLE
- 기본적으로 main 메소드 호출하여 ApplicationContext를 설정합니다.
- 만약 main 메서드가 없어 호출하지 못하면 기본 매커니즘으로 ApplicationContext를 설정합니다
Excluding Test Configuration
- @TestConfiguration은 내부 클래스로 사용되거나 상위 수준 클래스로 사용됩니다.
- 상위 수준 클래스에 붙이면, src/test/java 의 클래스를 자동으로 스캔하지 않도록 합니다.
- @Import를 통해 @TestConfiguration를 적용할 수 있습니다.
- @ComponentScan으로 설정 클래스를 스캔하여 적용할 수 있습니다.
- 제외하고자 하는 설정 클래스는 excludeFilters 속성에 TypeExcludeFilter 클래스를 활용하여 제외할 수 있습니다.
Testing With a Mock Environment
- Spring MVC를 MockMvc (or WebTestClient)를 가지고 mocking 하여 테스트할 수 있습니다.
- mocking은 Spring MVC 에만 일어나므로 low-level인 servlet container는 mocking 되지 않습니다.
Testing With a Running Server
- 테스트 환경에서 running server를 띄우고자 할 경우, RANDOM_PORT 사용이 권장됩니다.
- @LocalServerPort를 통해 현재 떠 있는 서버의 포트를 주입받을 수 있습니다.
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
class MyRandomPortWebTestClientTests {
@Test
void exampleTest(@Autowired WebTestClient webClient) {
webClient
.get().uri("/")
.exchange()
.expectStatus().isOk()
.expectBody(String.class).isEqualTo("Hello World");
}
}
- WebTestClient를 통해 테스트 중에 요청과 응답을 연속적으로 할 수 있는 API 제공합니다
- 서버 요청 및 응답이 편리해집니다.
- ApplicationContext에 주입받아 사용할 수 있습니다.
Customizing WebTestClient
- WebTestClientBuilderCustomizer를 재정의하고 빈으로 등록하여, WebTestClient를 커스터마이징 할 수 있습니다.
Mocking and Spying Beans
- 테스트 환경에서 Bean을 Mocking 하는 경우가 있습니다.
@MockBean
- @MockBean 어노테이션을 붙여 bean을 Mockito mock로 정의할 수 있습니다.
- class-level : 새로운 mock bean이 등록됩니다.
- field-level : 존재하는 bean의 definition을 mock bean의 definition 교체하여 주입합니다.
- 테스트 후 Mock Bean의 definition은 원래의 bean definition으로 자동적으로 교체됩니다.
- 기본적으로 ApplicationContext는 test간 공유됩니다
- 테스트 후 Mock Bean은 원래의 bean 으로 리셋되므로 test마다 ApplicationContext가 refresh 됩니다
- 이로 인해 테스트 성능이 낮아질 수 있습니다.
- Mock Bean은 Bean 초기화 로직이 적용됩니다.
- Bean 초기화 후 definition을 교체하는 과정으로 생성되기 때문입니다.
- CGLib 라이브러리를 통해 Proxy객체로 생성되면 final 메서드는 mocking 할 수 없습니다.
- inline mock maker를 사용하여 Mockito를 설정할 경우 허가될 수 있습니다.
@SpyBean
- 특정 메서드의 동작을 mocking하고 mocking 되지 않은 메서드는 그대로 동작하는 Bean.
AopTestUtils.getTargetObject(proxySpy)
- Bean이 Proxy 기반으로 동작할 경우 기대치 설정 시에 Proxy를 제거해야 합니다.
- Proxy Bean으로부터 실제 Bean을 가져옵니다.
- 캐싱
- SpyBean의 메서드 중, @Cacheable 메서드가 있다면 애플리케이션 실행 시 -parameters 옵션을 사용합니다.
- 컴파일 타임 : @Cacheable 메서드의 parameter 이름은 Bean class의 class파일에 저장됩니다.
- 런타임 : class 파일에 접근하여 parameter이름을 얻어 리플렉션을 통해 parameter 변수의 값을 얻습니다.
- parameter를 key로 한 cache 값은 애플리케이션에서 사용중인 cache infrastructure에 보관됩니다.
- SpyBean의 메서드 중, @Cacheable 메서드가 있다면 애플리케이션 실행 시 -parameters 옵션을 사용합니다.
Auto-configured Test
- auto-configuration 기술은 편리하지만 때로는 테스트 환경보다 더 많은 설정관련 동작을 수행합니다.
- 테스트 환경에 딱 알맞는 자동설정만 할 수 있도록 test slice 기능을 제공합니다.
@*Test
// ...
@AutoConfigureCache
@AutoConfigureDataJdbc
@AutoConfigureTestDatabase
@ImportAutoConfiguration
public @interface DataJdbcTest {
- spring-boot-test-autoconfigure 모듈은 test slice를 자동적으로 설정하기 위한 어노테이션들을 제공합니다.
- @*Test 의 패턴의 이름을 가집니다.
- 메타 어노테이션 입니다. (@AutoConfigure*, @ImportAutoConfiguration)
- 적절한 컴포넌트를 스캔하여 모듈에 맞는 자동설정을 동작시키고 ApplicationContext를 로드합니다.
- @*Test 어노테이션은 함께 사용되는 것을 지원하지 않습니다.
- multiple slice를 구성하고자 할 경우, @*Test와 @AutoConfigure*를 조합해서 사용하세요.
@AutoConfigure*
// ...
@ImportAutoConfiguration
public @interface AutoConfigureDataJdbc {
- 특정 모듈의 자동 설정을 동작하도록 하는 어노테이션 입니다.
- @AutoConfigure*는 @SpringBootTest와도 사용할 수 있습니다.
@ImportAutoConfiguration
// ...
@Import(ImportAutoConfigurationImportSelector.class)
public @interface ImportAutoConfiguration {
- @AutoConfiguration* 을 import하는 어노테이션 입니다.
- ImportAutoConfigurationImportSelector 대상 @AutoConfiguration*가 ImportCandidates에 보관됩니다.
- 설정할 수 있는 자동설정 클래스는 ImportCandidates의 *AutoConfiguration set으로 제한됩니다.
- include, exclude 속성도 ImportCandidates 내의 *AutoConfiguration 클래스만 유효하게 동작합니다.
Auto-configured JSON Tests
@JsonTest
// ...
@AutoConfigureCache
@AutoConfigureJson
@AutoConfigureJsonTesters
@ImportAutoConfiguration
public @interface JsonTest {
- JSON 오브젝트를 직렬화 / 역직렬화하는데 사용되는 자동설정 기능을 제공합니다.
- Jackson, Gson, Jsonb 라이브러리를 포함하여 다양하게 제공합니다.
@JsonTest
class MyJsonTests {
@Autowired
private JacksonTester<VehicleDetails> json;
@Test
void serialize() throws Exception {
VehicleDetails details = new VehicleDetails("Honda", "Civic");
assertThat(this.json.write(details)).isEqualToJson("expected.json");
assertThat(this.json.write(details)).hasJsonPathStringValue("@.make");
assertThat(this.json.write(details)).extractingJsonPathStringValue("@.make").isEqualTo("Honda");
}
@Test
void deserialize() throws Exception {
String content = "{\"make\":\"Ford\",\"model\":\"Focus\"}";
assertThat(this.json.parse(content)).isEqualTo(new VehicleDetails("Ford", "Focus"));
assertThat(this.json.parseObject(content).getMake()).isEqualTo("Ford");
}
}
- Support 클래스를 제공합니다 (JacksonTest, GsonTester, JsonbTester)
@Test
void someTest() throws Exception {
SomeObject value = new SomeObject(0.152f);
assertThat(this.json.write(value)).extractingJsonPathNumberValue("@.test.numberValue")
.satisfies((number) -> assertThat(number.floatValue()).isCloseTo(0.15f, within(0.01f)));
}
- AssertJ-based helper 라이브러리를 제공합니다. (JSONAssert, JsonPath)
- satisfies : 값이 주어진 조건과 매칭되는지 단언하는 API 입니다.
Auto-configured Spring MVC Tests
@WebMvcTest
@AutoConfigureCache
@AutoConfigureWebMvc
@AutoConfigureMockMvc
@ImportAutoConfiguration
public @interface WebMvcTest {
- Spring MVC Controller의 자동설정을 제공합니다.
- Spring MVC infrastructure를 auto-configure합니다
- @Controller, @ControllerAdvice, @JsonComponent, Converter, GenericConverter, Filter, HandlerInterceptor, WebMvcConfigurer, WebMvcRegistrations, HandlerMethodArgumentResolver
- (@Component, @ComfigurationProperties 빈들은 스캔되지 않습니다)
MockMvc
@WebMvcTest(UserVehicleController.class)
class MyControllerTests {
@Autowired
private MockMvc mvc;
@MockBean
private UserVehicleService userVehicleService;
@Test
void testExample() throws Exception {
given(this.userVehicleService.getVehicleDetails("sboot"))
.willReturn(new VehicleDetails("Honda", "Civic"));
this.mvc.perform(get("/sboot/vehicle").accept(MediaType.TEXT_PLAIN))
.andExpect(status().isOk())
.andExpect(content().string("Honda Civic"));
}
}
- Spring MVC의 테스팅을 지원하는 프레임워크 입니다.
- Http Server를 띄우지 않고 Http 요청을 dispatch하고 결과를 검증할 수 있습니다.
- 주로 컨트롤러와 뷰를 테스트하는데 사용됩니다.
@AutoConfigurerMockMvc
// ...
@ImportAutoConfiguration
@PropertyMapping("spring.test.mockmvc")
public @interface AutoConfigureMockMvc {
- MockMvc를 auto-configure합니다.
- MockMvc 자동설정을 커스터마이징 할 경우, @AutoConfigurerMockMvc의 attribute를 셋팅하세요.
- example) 필터 추가, webclient 사용 등
@WithMockUser (Spring Security)
- Spring Security를 사용하면 기본적으로 WebSercurityConfigurer를 스캔합니다.
- 간단한 설정으로 테스트에 주요 기능만 테스트하도록 인증 어노테이션을 지원합니다.
Auto-configured Data JPA Tests
@DataJpaTest
- JPA 테스트 관련 auto-configuration 기능을 제공합니다.
- @Entity 클래스 스캔
- Spring Data JPA Repository를 자동설정 합니다.
- (embedded database 사용시 자동설정)
- SQL query log 셋팅
- (attribute) showSql
- (property) spring.jpa.show-sql
- @Component, @ComfigurationProperties 빈들은 스캔되지 않습니다
Transaction
- data JPA 의 각 test는 기본적으로 transcational 하여 테스트 블록을 모두 실행한 후 롤백됩니다.
@Transactional(propagataion = Propagation.NOT_SUPPORTED)
- transaction 사용을 끌 수 있습니다
TestEntityManager
- 테스트용 EntityManager 입니다.
- @AutoConfigureTestEntityManager를 붙여서 auto-configure를 동작시킬 수 있습니다.
@AutoConfigureTestDatabase
- 테스트중에 사용되는 database를 자동설정하는 어노테이션 입니다.
- replace attribute를 사용하여 테스트에 연결할 database를 지정할 수 있습니다.
- (default) ANY : 테스트용 datasource로 교체합니다. (default: in-memory)
- NONE : 교체하지 않습니다. (기존에 사용하던 datasource 사용)
- AUTO_CONFIGURE : auto-configure 기능으로 만들어진 datasource로 교체합니다.
참고
'Spring > Spring Boot' 카테고리의 다른 글
[Spring Boot] 3. Web: Servlet Web Application (0) | 2023.10.12 |
---|---|
[Spring Boot] 2-10. Core Features: Auto-Configuration (0) | 2023.10.11 |
[Spring Boot] 2-6. Core Features: JSON (0) | 2023.10.09 |
[Spring Boot] 2-3. Core Features: Profile (0) | 2023.10.09 |
[Spring Boot] 2-2. Core Features: Externalized Configuration (2) | 2023.10.09 |