애플리케이션은 여러 개의 계층으로 나누어져 있다. 하나의 애플리케이션은 계층별로 역할이 있고, 계층별로 서로 연동되기 때문에 각각의 계층 별로 잘 동작하는지 테스트를 진행한 후에 마지막으로 통합 테스트를 통해서 계층 간의 연동에 문제가 없는지 확인해야 비로소 개발자의 테스트 작업이 마무리 되는것이라고 할 수 있다.
이처럼 개발자가 각 계층에 구현해 놓은 기능들이 잘 동작하는지 특정 계층만 잘라서(Slice) 테스트하는 것을 슬라이스 테스트(Slice Test)라고 한다.
@SpringBootTest // (1)
@AutoConfigureMockMvc // (2)
public class ControllerTestDefaultStructure {
// (3)
@Autowired
private MockMvc mockMvc;
// (4)
@Test
public void postMemberTest() {
// given (5) 테스트용 request body 생성
// when (6) MockMvc 객체로 테스트 대상 Controller 호출
// then (7) Controller 핸들러 메서드에서 응답으로 수신한 HTTP Status 및 response body 검증
}
}
(1)의 @SpringBootTest
애너테이션은 Spring Boot 기반의 애플리케이션을 테스트 하기 위한 Application Context를 생성한다.
Application Context에는 애플리케이션에 필요한 Bean
객체들이 등록되어 있다.
(2)의 @AutoConfigureMockMvc
애너테이션은 Controller 테스트를 위한 애플리케이션의 자동 구성 작업을 해준다.
Spring Boot의 자동 구성을 통해 애플리케이션의 설정을 손쉽게 사용하듯이 @AutoConfigureMockMvc
애너테이션을 추가함으로써 테스트에 필요한 애플리케이션의 구성이 자동으로 진행된다.
(3)의 MockMvc 같은 기능을 사용하기 위해서는 @Autowired
애너테이션을 추가해야 한다.
(3)에서 DI로 주입 받은 MockMvc는 Tomcat 같은 서버를 실행하지 않고 Spring 기반 애플리케이션의 Controller를 테스트 할 수 있는 완벽한 환경을 지원해주는 일종의 Spring MVC 테스트 프레임워크다.
MockMvc 객체를 통해 우리가 작성한 Controller를 호출해서 손쉽게 Controller에 대한 테스트를 진행할 수 있다.
(1), (2), (3)을 통해 Controller를 테스트 할 준비가 되었다.
이제 (4)와 같이 테스트 하고자 하는 Controller 핸들러 메서드의 테스트 케이스를 작성하면 된다.
Postman을 사용해서 Controller에 요청을 하기 위해서는 reqeust body 데이터가 필요하다. Controller를 테스트 하기 위해서는 (5)의 단계에서 테스트용 request body를 직접 만들어 주어야 한다.
Given-When-Then 패턴에서 Given에 해당된다.
(6)에서는 MockMvc 객체를 통해 요청 URI와 HTTP 메서드등을 지정하고, (5)에서 만든 테스트용 request body를 추가한 뒤에 request를 수행한다.
Given-When-Then 패턴에서 When에 해당된다.
(7)에서는 Controller에서 전달 받은 HTTP Status와 response body 데이터를 통해 검증 작업을 진행한다.
Given-When-Then 패턴에서 Then에 해당된다.
@SpringBootTest
@AutoConfigureMockMvc
class MemberControllerTest {
@Autowired
private MockMvc mockMvc;
@Autowired
private Gson gson;
@Test
void postMemberTest() throws Exception {
// given (1)
MemberDto.Post post = new MemberDto.Post("hgd@gmail.com",
"홍길동",
"010-1234-5678");
String content = gson.toJson(post); // (2)
// when
ResultActions actions =
mockMvc.perform( // (3)
post("/v11/members") // (4)
.accept(MediaType.APPLICATION_JSON) // (5)
.contentType(MediaType.APPLICATION_JSON) // (6)
.content(content) // (7)
);
// then
MvcResult result = actions
.andExpect(status().isCreated()) // (8)
.andReturn(); // (9)
// System.out.println(result.getResponse().getContentAsString());
}
}
1. Given
Postman을 사용할 때 request body에 포함시키는 요청 데이터와 동일한 역할을 한다.
Postman에서 JSON 포맷으로 request body에 작성하는데, (2)에서 작성된 내용을 JSON 포맷으로 변환해준다.
Gson 라이브러리를 사용하기 위해서는 build.gradle의
dependencies {...}
에implementation 'com.google.code.gson:gson'
를 추가해 주어야 한다.
2. When
MockMvc로 테스트 대상 Controller의 핸들러 메서드에 요청을 전송하기 위해서는 기본적으로 (3)과 같이 perform()
메서드를 호출해야 하며 perform()
메서드 내부에 Controller 호출을 위한 세부적인 정보들이 포함된다.
(4) - (7) 까지는 HTTP request에 대한 정보이며, MockMvcRequestBuilders 클래스를 이용해서 빌더 패턴을 통해 request 정보를 채워 넣을 수 있다.
(4)에서 post() 메서드를 통해 HTTP POST METHOD와 request URL을 설정한다.
(5)에서 accept() 메서드를 통해 클라이언트 쪽에서 리턴 받을 응답 데이터 타입으로 JSON 타입을 설정한다.
(6)에서 contentType() 메서드를 통해 서버 쪽에서 처리 가능한 ContentType으로 JSON 타입을 설정한다.
(7)에서 content() 메서드를 통해 request body 데이터를 설정한다.
request body에 전달하는 데이터는 (2)에서 Gson 라이브러리를 이용해 변환된 JSON 문자열이다.
3. Then
MockMvc의 perform() 메서드는 ResultActions 타입의 객체를 리턴하는데, 이 ResultActions 객체를 이용해서 우리가 전송한 request에 대한 검증을 수행할 수 있다.
(8)에서 andExpect() 메서드를 통해 파라미터로 입력한 매처(Matcher)로 예상되는 기대 결과를 검증할 수 있다.
(8)에서는 status().isCreated()
를 통해 response status가 201(Created)
이 맞는지 검증하고 있다.
(9)에서 andReturn()을 통해서 response 데이터를 확인할 수 있는데, 디버깅 용도로 response로 전달되는 응답 데이터를 출력할 때 사용할 수 있다. 일반적으로는 (8)까지의 검증 과정에서 테스트는 끝나게 된다.
맨 마지막 라인에 있는
System.out.println(result.getResponse().getContentAsString());
을 주석 해제한 후에 response body로 전달된 JSON 데이터에서 한글이 깨져 보일 경우, application.yml 파일에 아래의 설정을 추가한다.
...
...
server:
servlet:
encoding:
force-response: true
여기서, 추가적으로 Controller에서 결과 값으로 되돌려 주는 response body 데이터를 검증해 보는 것이 제일 확실한 검증 방법일 것이다.
@SpringBootTest
@AutoConfigureMockMvc
class MemberControllerTest2 {
...
...
@Test
void postMemberTest() throws Exception {
// given
// when
// then
MvcResult result = actions
.andExpect(status().isCreated())
.andExpect(jsonPath("$.data.email").value(post.getEmail())) // (1)
.andExpect(jsonPath("$.data.name").value(post.getName())) // (2)
.andExpect(jsonPath("$.data.phone").value(post.getPhone())) // (3)
.andReturn();
System.out.println(result.getResponse().getContentAsString());
}
}
MemberController에 대한 조금 더 확실한 테스트 검증을 위해서 MemberController의 postMember() 핸들러 메서드에서 리턴하는 response body(JSON 형식)의 각 프로퍼티(email, name, phone)의 값을 검증하는 기능을 추가했다.
MockMvcResultMatchers 클래스에서 지원하는
jsonPath()
를 사용하면 JSON 형식의 개별 프로퍼티에 손쉽게 접근할 수 있다.