현재 담당 기능 기준 약 80% 이상의 단위 테스트 커버리지를 유지하고 있다. 이제 이 검증된 메서드들이 전체적으로 잘 상호작용 할 수 있는지 검증할 시점이라고 판단했다.
단위 테스트를 통해 메서드가 정상적으로 동작 하는 것을 확인 했다면, 이제 통합 테스트를 통해 검증된 개별 메서드들이 시스템 전체에서도 정상적으로 작동하는지 확인 할 차례다. 단위 테스트는 정말 필요한 의존성만 주입 받아 테스트의 범위를 줄여 하나의 메서드에 집중하는 반면, 통합 테스트는 애플리케이션 전체 관점에서 위 단위 테스트에서 검증 한 메서드들이 올바르게 동작 하는지 검증할 수 있다.
Spring에서의 통합 테스트는 모든 빈을 컨테이너에 올린 뒤 테스트를 실행하기 때문에 더욱 실제 배포 환경과 유사한 환경에서 테스트를 진행할 수 있다. 이로 인해 실행 시간이 다소 소요된다는 단점이 존재한다.
한번 코드를 살펴보도록 하겠다.
@SpringBootTest
@AutoConfigureMockMvc
@Import(TestSecurityConfig.class)
@Transactional
public class SearchIntegrationTest {
@Autowired
protected MockMvc mockMvc;
@Autowired
private TestSetUp testSetUp;
@Test
@WithMockUser(username = "1")
@DisplayName("스터디 조회")
void 스터디_조회() throws Exception {
// given
testSetUp.initStudy();
int page = 0;
int size = 10;
StudySortBy sortBy = StudySortBy.ALL;
// when
ResultActions perform = mockMvc.perform(get("/spot/search/studies/all/no-conditions")
.param("page", String.valueOf(page)) // 쿼리 파라미터 전달
.param("size", String.valueOf(size)) // 쿼리 파라미터 전달
.param("sortBy", sortBy.toString())); // 쿼리 파라미터 전달
// then
perform
.andExpect(status().isOk())
.andExpect(jsonPath("$.isSuccess").value(true));
}
}
위 패턴은 테스트를 구조화 하고, 보다 더 테스트 코드를 간결하고 직관적으로 표현할 수 있는 패턴이다. 아주 간단하게 설정, 실행, 검증
3가지 단계로 나뉜다.
먼저, Given 단계에서는 행동 전 시스템의 상태를 설명한다. 즉, 테스트의 사전 조건 또는 초기 상태를 명시한다. 다음 When은 테스트할 행동을 설명한다. 마지막으로 Then은 사용자의 행동으로 인해 발생해야 하는 변화를 설명하는 단계다.
이렇게 글로 보면 이해하기 어렵다고 생각할 수 있지만, 예시로 정리하면 보다 더 쉽게 이해할 수 있다.
Given 사용자가 로그인 페이지에 있다.
When 사용자가 올바른 이메일과 비밀번호를 입력하고 로그인 버튼을 클릭한다.
Then 사용자는 대시보드로 리다이렉트된다.
위 패턴으로 테스트 코드를 작성하게 되면 보다 더 직관적이고 읽기 쉽게 작성될 뿐 아니라, 개발자가 아닌 사람도 보다 더 쉽게 테스트 시나리오를 이해할 수 있다는 장점이 있다.
단위 테스트 코드를 작성 하면서 해당 패턴을 적용 했는데, 만족도가 높았다!
(참고 : https://martinfowler.com/bliki/GivenWhenThen.html)
이제 코드에 대해 살펴보도록 하겠다.
@SpringBootTest
: Spring Boot 애플리케이션의 전체 컨텍스트를 로드하여 테스트를 진행한다. 이로 인해 실제 실행 환경과 동일한 환경을 설정할 수 있다. @AutoConfigureMockMvc
: MockMvc
를 설정하여 테스트를 진행하기 위해 필요한 애노테이션이다. 실제 HTTP 요청을 보내는 대신 MockMvc
를 사용하여 테스트한다.@Import(TestSecurityConfig.class)
: 특정 설정 클래스를 테스트 환경에 Import하는 애노테이션이다. SPOT의 경우, 소셜 로그인을 위해 Spring Security를 도입 했는데, 테스트 환경에서는 이를 적절히 비활성화 시키기 위해 사용했다. @Transactional
: 테스트 실행 후, 데이터베이스 상태를 Rollback 하기 위해 사용하는 애노테이션이다. 테스트 중 DB에 변경이 생기더라도, 종료 후 이를 Rollback하여 테스트 독립성을 유지할 수 있다. 이후, MockMvc
를 통해 HTTP 요청을 시뮬레이션하여 테스트를 진행한다. 위 코드에서는 GET
방식으로 /spot/search/studies/all/no-conditions
URL을 호출한다.
param()
을 통해 Query Parameter를 추가할 수 있고, contentType(), content()
를 통해 컨텐츠 타입이나 Body 데이터를 지정하여 테스트 할 수 있다.
해당 메서드가 반환하는 타입은 ResultActions
으로, Spring MVC 테스트에서 HTTP 요청을 실행한 결과에 대한 후속 작업을 수행하는 객체이다. 응답 상태 코드 확인, JSON 구조 검증, 응답 내용에 대한 추가 조건 등을 검사할 수 있다.
위 코드의 경우에는 응답 코드가 200인지와 응답 내 isSuccess
값이 true
인지 검증하고 있다.
해당 포스팅은 통합 테스트를 작성 해봐야겠다는 다짐을 한 이후부터 첫 테스트 통과 까지의 학습 기록을 정리한 글이다. 처음 해보는 작업이라 아직 공부해야 할 부분이 많다. 출시 이전까지 통합 테스트 코드를 작성 하며 보다 더 깊이 있는 학습을 하고, 이를 정리해보도록 하겠다.