이번 포스트에서는 @RestController에 대한 단위테스트에 대해서 살펴보자.
REST를 위한 단위 테스트
rest 동작을 확인하기 위해서는 두 가지 형태로 살펴볼 수 있다.
일단 일반적인 호출이 되는가에 대한 것은 앞서 살펴봤던 Controller Test와 유사하다. 추가로 생각할 만한 것은 응답의 타입에 대한 정도이다.
andExpect(content().contentType(MediaType.APPLICATION_JSON))
하지만 REST는 model이나 session에 데이터를 담지 않기 때문에 어떤 정보가 전달되었는지 즉 내용을 확인하기 위한 방법이 필요하다. 이를 위해 RestController의 단위 테스트에는 com.jayway.jsonpath가 주로 사용된다.
관련 문서는 https://github.com/json-path/JsonPath를 참조한다.
json-path를 위해서는 다음의 library 가 필요한데 스프링 boot에서는 spring-boot-starter-test에서 이미 포함하고 있으므로 추가 설정은 필요 없다.
<!-- https://mvnrepository.com/artifact/com.jayway.jsonpath/json-path -->
<dependency>
<groupId>com.jayway.jsonpath</groupId>
<artifactId>json-path</artifactId>
<version>2.7.0</version>
</dependency>
주요 표현 예
다음은 https://github.com/json-path/JsonPath에서 제시하는 표현식 중 자주 사용되는 표현식에 대한 예이다. 추가적인 표현식은 원문을 참조하기 바란다.
표현 |
의미 |
$ |
전달받은 json 객체의 root element |
$.name |
root element의 name 속성 |
$.hobby[2] |
root element 중 hobby 속성이 배열이고 2번 index 요소 |
$.hobby[2].name |
root element의 hobby 속성 중 2번 index의 name 속성 |
$.hobby.length() |
root element의 hobby 속성의 길이 |
테스트 해보기
@RestController
다음은 간단하게 Board 정보를 R.E.S.T로 반환하는 controller이다.
@RestController
@RequestMapping("/api")
@RequiredArgsConstructor
public class SimpleRestController {
private final BoardService service;
@GetMapping("/board/{boardNo}")
public Board getBoard(@PathVariable Long boardNo) {
Optional<Board> result = service.select(boardNo);
if (result.isPresent()) {
return result.get();
} else {
return null;
}
}
@PostMapping("/board")
public Board registBoard(@RequestBody Board board) {
return service.write(board);
}
}
SimpleRestController는 BoardService를 의존성으로 가지므로 테스트 과정에서 Mock으로 처리해야 한다.
테스트
기본적으로 MockMVC를 사용하는 과정에는 차이가 없다. 단지 body에 전달된 객체를 사용하기 위해서 jsonPath의 메서드를 사용하는 부분과 Matcher로 hamcrest를 사용하는 부분만 신경쓰면 된다.
@WebMvcTest({ SimpleRestController.class })
public class T_9315_WebMvcTest2 {
@Autowired
MockMvc mockMvc;
@MockBean
BoardService service;
@Test
public void getBoardTest() throws Exception {
// given
String url = "/api/board/1";
when(service.select(1L)).thenReturn(Optional.empty());
// when
ResultActions actions = mockMvc.perform(get(url));
// then
actions.andExpect(status().isOk())
.andExpect(MockMvcResultMatchers.content().string(""))
.andDo(print());
}
@Test
public void registBoardTest() throws Exception {
// given
String url = "/api/board";
Board board = Board.builder().boardNo(1L).writer("hong").content("Hello").build();
String json = new ObjectMapper().writeValueAsString(board);
when(service.write(board)).thenReturn(board);
// when
ResultActions actions = mockMvc.perform(post(url)
.contentType(MediaType.APPLICATION_JSON).content(json));
// then
actions.andExpect(status().isOk())
.andExpect(header().string("content-type", "application/json"))
.andExpect(jsonPath("$.writer", equalTo("hong")))
.andExpect(jsonPath("$.boardNo", equalTo(1)))
.andDo(print());
}
}