참고
Mocking and Spying BeansSpring boot Auto-configured TestsRepository Layer 단위 테스트Service Layer 단위 테스트Controller Layer 테스트MockMvcResponse를 Object로 파싱하기@WebMvcTest (단위테스트)Mocking UserSpring Security Filter 제외 방법multipart/form-data request 하기Spring Test Framework의 cache를 이용한 테스트 코드 최적화@Transactional 사용 주의1. 자동 롤백 됨2. @Transactional이 없을 때 테스트가 통과하지 않을 수 있음Test Configuration 찾기@TestConfigurationTestContainer 이용하기
Mocking and Spying Beans
final
method를 mock하기 위해서는org.mockito:mockito-inline
을 application의 test dependency에 추가하기
Spring boot Auto-configured Tests
- object의 JSON serialization 과 deserialization 테스트를 하기 위해서는 @JsonTest를 이용가능함
@DataJpaTest
는 JPA 어플리케이션을 테스트하기 위해 사용함.@Entity
클래스를 스캔하고 Spring Data JPA repository 들을 configure함
@..Test 어노테이션을 하나의 테스트에 여러개 붙이는건 지원하지 않는 대신, 여러 slice test를 하기 위해서는 하나의 @...Test 어노테이션을 선택하고 @AutoConfigure.. 어노테이션을 이용하기
만약 @AutoConfigure.. 어노테이션을 @SpringBootTest 어노테이션과 함께 활용한다면 application의 slice 테스트를 위한 것은 아니고 auto-configured test bean의 일부만을 원할 때 이렇게 사용가능함
Repository Layer 단위 테스트
@DataJpaTest
- 디폴트로
@Entity
클래스와Spring Data JPA Repository
를 스캔함
- classpath에 embedded database가 이용가능하면 그것 configure 해줌
- 디폴트로 transactional이 적용되고 각각의 테스트 별로 rollback이 됨
- 표준 JPA
EntityManager
를 대신하여 테스트에 맞게 특별히 디자인 된TestEntityManager
빈을 주입시켜줌
Service Layer 단위 테스트
예제 코드
Controller Layer 테스트
MockMvc
Response를 Object로 파싱하기
- TypeReference를 이용해 mockMvc response를 object로 매핑하기
BaeldungGet JSON Content as Object Using MockMVC | Baeldung
Get JSON Content as Object Using MockMVC | Baeldung
Explore several ways to get JSON content as an object using MockMVC and Spring Boot.
@WebMvcTest (단위테스트)
- 해당 어노테이션을 사용하면 Spring Boot는 전체 컨텍스트를 실행하는 것이 아닌 web layer만 실행함. 다수의 controller중 하나만 생성(instantiated)도 가능함
- Spring MVC controller가(얘만) 생각대로 작동하는지를 테스트하기 위함임
@Controller
@ControllerAdvice
@JsonComponent
Converter
GenericConverter
Filter
HandlerInterceptor
WebMvcConfigurer
HandlerMethodArgumentResolver
- @Component, @ConfigurationProperties 와 같은 Bean들은 컴포넌트 스캔 되지 않음
- Controller에 대한 단위 테스트이기에 Controller의 의존성인 Service는
@MockBean
을 이용하여 mock 객체로 주입
Spring MVC 의 infrastructure를 auto-configure하며 아래 리스트의 빈들을 스캔 ( Filter, Interceptor, Converter, @Controller
등의 빈들이 스캔됨 ) [참고 공식 문서 ]
- @WebMvcTest는 기본적으로 MockMvc와 Spring Security를 auto configure 함(DefaultSecurityFilterChain이 등록됨)
여기에 CsrfFilter도 포함되어, post 요청 보낼 시에는 with(csrf)를 붙여서 써주어야 함 (안붙이면 403 Forbidden
에러 발생)
Mocking User
[ Spring doc ] Runnning a test as a user in Spring MVC test
[ Spring doc ]Testing Method Security (@WithMockUser 등 어노테이션 사용방법 정리)
- @WithMockUser, @WithSecurityContext 등
Spring Security Filter 제외 방법
@WebMvcTest 에서 excludeAutoConfiguration 설정에 SecurityAutoConfiguration을 설정하기
@WebMvcTest(controllers = {DepartmentController.class}, excludeAutoConfiguration = {SecurityAutoConfiguration.class})
- 또는
MockMvcBuilders
를 이용해서 custom 하게 만들면서 SecurityFilter는 제외
webEnvironment
:RANDOM_PORT, TestRestTemplate
: 실제로 애플리케이션 전체를 구동하는 테스트 방법
@AutoconfigureMockMvc @SpringBootTest
: 애플리케이션을 실행하지 않고, Spring이 HTTP request를 handle 하고 controller에 넘겨주는 것 까지만 테스트 하는 방법
MockMvc
가 주입되기 위해서는 @SpringBootTest
에서는 @AutoConfigureMockMvc
가 사용되어야 함 && WebEnvironMent None이 되면 해당 MockMvc 못찾음
- WebEnvironment (참고 : 스프링 부트 테스트)
- RANDOM_PORT
- MOCK : 내장 톰캣 구동 안함
- DEFINED_PORT
- NONE
MockMvcBuilders
를 이용하여 MockMvc 커스텀 정의하는 방법
[ Spring Security Docs ] Setting up MockMvc and Spring Security
SecurityMockMvcConfigurers.springSecurity()
요거 apply 안해주면 mvc 가 Sprint Security Filter를 포함하지 않아서 security에 대한 테스트 안됨
- To use Spring Security with Spring MVC Test, add the Spring Security
FilterChainProxy
as aFilter
- You also need to add Spring Security’s
TestSecurityContextHolderPostProcessor
to support Running as a User in Spring MVC Test with Annotations. To do so, use Spring Security’sSecurityMockMvcConfigurers.springSecurity()
@SpringBootTest
에서 특정 Bean 제외하는 방법@EnableAutoConfiguration
의exclude
프로퍼티 사용 (AutoConfiguration 제외)- 해당 Bean을 TestConfig로 null로 등록하면, 해당 Bean이 대체됨
@SpringBootTest(classes = { TestConfig.class })
JsonPath
사용법 참고 : Jayway JsonPath Github
- mockMvc를 이용하여 반환된 json이 null인지 확인하는 방법
andExpect(jsonPath("$").doesNotExist())
- array 의 특정 필드를 뽑아내는 방법
- jsonPath(
"$.data[*].role"
) : data 안에 리스트의 모든 원소의 role 필드를 뽑아내서 JSONArray로 만들어줌 - 그후 해당 array를 비교하는 방법
- root Array의 각 아이템별 모든 프로퍼티를 뽑아내는건 아무리 해도 잘 안됐음. objectMapper 통해서 deserializing 해서 비교
- 봤었던 Github issue. How to traverse array which is the root element
- 값이 다르다는 것을 비교할 때
- 이 때 Matchers는 org.hamcrest 것.
꿀팁
- 에러 메시지 확인을 위해서는 아래와 같이 이용
andExpect(MockMvcResultMatchers.
status
().reason("asdfasdf"))
multipart/form-data request 하기
[ 블로그] 참고
Spring Test Framework의 cache를 이용한 테스트 코드 최적화
[ Spring Framework ] Context Caching
org.springframework.test.context.cache
모듈의 log level을DEBUG
로 설정하면 context cache의 statistics를 확인할 수 있음
- 테스트 간에 설정이 공통이라면 Spring Test Framework는 ApplicationContext를 다시 만들지 않고 캐시된 context를 활용함
- 그러나, 같은 설정을 상속하더라도 하위 클래스에서 MockBean 같은 필드가 추가되면 ApplicationContext가 다시 만들어진다고 함
- 그리고 Acceptance Test에 대해서 DB 환경을 공유함 (Get 메서드들에 대해서는)

@Transactional 사용 주의
1. 자동 롤백 됨
테스트가 @Transactional이면 각 테스트 메서드 수행별로 roll back을 디폴트로 하게 됨. 그러나 @SpringBootTest의 webEnvironment attribute가 RANDOM_PORT와 DEFINED_PORT인 경우는 실제 servlet 환경이어서 Http client와 서버가 서로 다른 스레드에서 동작하게 됨. 그래서 다른 Transaction이고 서버에서 시작된 transaction은 roll back 이 되지 않음
2. @Transactional이 없을 때 테스트가 통과하지 않을 수 있음
- 서비스 코드에서 @Transactional을 안 붙이고, 테스트 코드에서 @Transactional을 붙이면 테스트는 통과하지만 런타임시 오류가 발생할 수 있다!
- 또한, 영속성 컨텍스트가 유지되기 때문에 영속성 컨텍스트를 없앴을 때에 테스트가 통과하지 않을 수도 있음
Test Configuration 찾기
- 찾는 방법
@ContextConfiguration
을 사용해서 어떤 @Configuration을 로드할지 설정- nested @Configuration을 테스트 클래스 안에 사용
@SpringBootTest
의 경우 classes 항목에 클래스를 넣어주면 알아서 해당 Component를 ApplicationContext를 만들 때 load하게 됨
- Document : The component classes to use for loading an ApplicationContext. Can also be specified using
@ContextConfiguration(classes=...)
. If no explicit classes are defined the test will look for nested @Configuration classes, before falling back to a @SpringBootConfiguration search.
@*Test
annotation들이 알아서 primary configuration을 찾아줌
- 만약에 primary configuration이 아닌 커스터마이징을 원한다면 nested
@TestConfiguration
을 활용하기
@TestConfiguration
@Configuration that can be used to define additional beans or customizations for a test.
Unlike regular
@Configuration
classes the use of @TestConfiguration
does not prevent auto-detection of @SpringBootConfiguration.- 테스트 환경에서 필요한 빈들을 등록할 수 있도록 도와주는 어노테이션
- 해당 어노테이션으로 클래스 생성후 이용하려면 Import 해서 이용해야함
- https://reflectoring.io/spring-boot-testconfiguration/
- 만약, 동일한 Bean을 Overriding 하고자 한다면
spring.main.allow-bean-definition-overriding
의 값을 true로 설정해야 함. 그렇지 않으면 BeanDefinitionOverrideException 발생
참고 코드
TestContainer 이용하기
[ Test Container ] Exposing host port is random
- 위 참고 링크에 따르면 test container의 host port는 랜덤으로 배정된다고 함. 특정시킬 수가 없음.