JUnit과 단위테스트
SpringBoot에서 JUnit를 사용하여 테스트 코드를 작성할때, 대표적으로 @SpringBootTest와 @WebMvcTest를 사용하는 경우가 많다.
처음에는 둘의 차이를 간단하게 @SpringBootTest는 모든 빈을 가져와서 속도가 느리고 @WebMvcTest는 필요한 빈만 가져와서 속도가 빠르다 정도로 이해를 했지만, 정확히 개념을 이해하지 못하고 사용한 탓에 테스트 케이스 작성 실패를 겪은적이 있었다. 내가 쓴 테스트 케이스가 어떤 상황에서, 무엇을 목적으로 돌아가는 테스트 케이스인지를 보다 정확히 이해하기 위해 개념을 정리한다.
@SpringBootTest + @AutoConfigureMockMvc
1) 특징
- 프로젝트 내부에 있는 스프링 빈을 모두 등록하여 테스트에 필요한 의존성을 추가
- 실제 운영 환경에서 사용될 클래스들을 통합하여 테스트
- 단위 테스트와 같이 기능 검증을 위한 것이 아니라, Spring Framework에서 전체적으로 Flow가 제대로 동작하는지 검증하기 위해 사용
2) 장점
- 애플리케이션의 설정, 모든 Bean을 모두 로드하기 때문에 운영환경과 가장 유사한 테스트가 가능
3) 단점
- 테스트 단위가 크기 때문에 디버깅이 까다로움
- 어플리케이션의 설정, 모든 Bean을 로드하기 때문에 시간이 오래 걸림
Q) @RunWith은 언제쓰나요?
만약 JUnit4를 사용한다면 @RunWith(SpringRunner.class)을 같이 추가해줘야지만 Annotation이 무시되지 않는다.
반면 JUnit5를 사용한다면 @RunWith, @ExtendWith 등을 추가해줄 필요가 없다.
Q) @AutoConfigureMockMvc는 언제쓰나요?
- Mock 테스트시 필요한 의존성을 제공
@Autowired
MockMvc mvc;
- MockMvc 객체를 통해 실제 컨테이너가 실행되는 것은 아니지만 로직상 테스트 가능
- print() 함수를 통해 좀 더 테일한 테스트 결과를 볼 수 있음
- @WebMvcTest가 아닌 @SpringBootTest에서도 Mock 테스트를 가능하게 해주는 역할
Q ) @ActiveProfiles는 언제쓰죠?
- 프로파일 전략을 사용 중이라면 원하는 프로파일 환경값을 설정 가능
@ActiveProfiles("test") // test 프로파일 사용
Q ) Mock이 뭐죠?
- 테스트 작성을 위한 환경 구축이 어렵거나, 테스트가 특정 경우 및 순간에 의존적인 경우 테스트 작성 시간을 단축하기 위해 사용
- 실제를 흉내낸 가짜 객체
- Mock 객체는 Mock일 뿐, 실제 객체를 작동했을 때 예상대로 작동하지 않을 수 있음
Q ) MockBean이 뭐죠?
- Mock 객체를 빈으로 등록
- Spring의 ApplicationContext는 Mock 객체를 빈으로 등록하며, 이미 동일한 타입의 동일한 객체가 빈으로 등록되어있을 경우, 해당 빈은 선언한 @MockBean으로 대체됨
[ 사용 예시 ]
@AutoConfigureMockMvc
@SpringBootTest
@ActiveProfiles("test")
public class FullfillmentControllerTest {
@Autowired
protected MockMvc mockMvc;
@MockBean
private FullfillmentProvisionService fullfillmentProvisionService;
@MockBean
private FullfillmentlicationService fullfillmentApplicationService;
when(fullfillmentProvisionService.provideItem(any())).thenThrow(new ItemNotFoundException(itemCode));
// then
mockMvc.perform(post("/v1.1/fullfillment/load")
.content(objectMapper.writeValueAsString(request))
.contentType(MediaType.APPLICATION_JSON)
)
.andExpect(status().isUnprocessableEntity())
.andExpect(jsonPath("$.header").exists())
.andExpect(jsonPath("$.body").isEmpty())
.andDo(print());
위 코드는 @SpringBootTest, @AutoConfogureMockMvc, 동시에 어찌보면 @WebMvcTest의 기능도 모두 포함된 테스트라고 해석될 수 있다.
- @SpringBootTest를 통해 어플리케이션 내부의 모든 Bean을 등록
- Mock테스트를 위해 @AutoConfigureMockMvc를 사용
- 이로써 MockMvc 객체를 통해 실제 컨테이너가 실행되는 것은 아니지만 로직상 테스트 가능
- @WebMvcTest가 아닌 환경에서도 Mock 테스트가 가능
- @ActiveProfiles를 통해 Alpha, Beta가 아닌 test 환경을 사용
- @MockBean을 사용해 2개의 객체를 Bean으로 등록
@WebMvcTest
1) 특징
- MVC를 위한 테스트. 컨트롤러가 예상대로 작동되는지 테스트하기 위해 사용됨
- Web Layer만 로드하며, @WebMvcTest 어노테이션을 사용시 아래의 항목들만 스캔하도록 제한하여 보다 빠르고 가벼운 테스트가 가능
(ex : @Controller, @ControllerAdvice, @JsonComponent, @Convert, @GenericConverter, Filter, WebMvcConfigurer, HandlerMethodArgumentResolver)
- Security, Filter, Interceptor, request / response Handling, Controller의 항목들만 스캔하도록 제한하며, @Component는 스캔 대상에서 제외
2) 장점
- WebApplication과 관련된 Bean들만 등록하기 때문에 @SpringBootTest보다 빠름
- 통합테스트를 진행하기 어려운 테스트를 개별적으로 진행 가능
(ex : 결제 모듈 API 사용에서 특정 조건에 따라 실패하는 경우를 Mock을 통해 가짜 객체를 만들어 테스트 가능)
3) 단점
- Mock을 기반으로 테스트 하기 때문에, 실제 환경에서는 예상 밖의 동작오류가 발생할 수 있음
[ 사용 예시 ]
@WebMvcTest(UserVehicleController.class)
public class MyControllerTests {
@Autowired
private MockMvc mvc;
@MockBean
private UserVehicleService userVehicleService;
@Test
public 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"));
}
}
위 코드는 TDD(Test-Driven Development)가 아닌 BDD(Behavior-Driven Development)로 동작하기 때문에 when().then()이 아닌 given().willReturn()이 사용되었다.
참고 자료
- 갓대희의 작은 공간 : @SpringBootTest로 통합 테스트 하기
- 스프링 공식 사이드 : Testing
'👩🏻💻 Programming > SpringBoot' 카테고리의 다른 글
NPE(Null Pointer Exception)를 방지하는 방법 (2) | 2022.12.27 |
---|---|
@JsonUnwrapped의 개념과 사용법 (0) | 2022.03.07 |
스프링 기본 정리 - 객체지향 원리 적용 (0) | 2022.02.08 |
SpringBoot에서 Naver API 연동하기 (2) | 2021.12.08 |
Spring Boot와 Exception 처리 (0) | 2021.12.07 |