왜 테스트 코드를 작성해야 하나요?
테스트 코드를 작성을 왜 하는지 궁금하신 분들이 계실겁니다.
물론 테스트 코드를 작성하지 않고 API를 만든 후에 포스트맨같은 툴로 API를 테스트 할 수도 있을겁니다. 저 역시 이전에는 그렇게 하였구요. 그러나 이 방법의 최대 단점은 손이 많이 간다는 것입니다.
예를 들어 회원가입을 한다면 확인해야 할 것이 아래와 같습니다.
- Email이 중복이 아닌가?
- 비밀번호가 양식에 맞는가?
- 필수로 입력해야할 칸이 비어있지는 않는가?
- 가입이 성공적으로 완료되었을때 응답을 올바르게 오는가?
- 1,2,3번에 해당하였을 경우 에러를 잘 뱉어내는가?
위와 같은 테스트 케이스들이 많습니다! 무려 회원가입 API 하나만 체크하는데도 최소 5개가 되는 케이스를 테스트 해야합니다.
이걸 포스트맨으로 일일히 옮겨 적는다면.. 예전 생각이 나서 정말 끔찍하네요.
이러한 노가다를 방지하기 위해 테스트 코드를 작성하는데요? 물론 테스트코드 역시 위와 같은 케이스를 모두 작성해주어야 하는 번거로움은 있습니다. 그러나 테스트코드를 작성하면 위와같은 케이스를 한번 클릭으로 모두 테스트할 수 있기때문에 시간 절약이 됩니다!
Controller Test
@ExtendWith(SpringExtension.class)
@SpringBootTest
@AutoConfigureMockMvc
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
class PostControllerTest {
@Autowired
private ObjectMapper objectMapper;
@Autowired
private MockMvc mockMvc;
@Autowired
private PostService postService;
public PostDTO initPostDTO() {
return PostDTO.builder()
.title("title")
.contents("contents")
.build();
}
@Test
@DisplayName("게시물 등록 테스트")
@Order(1)
public void createPost() throws Exception {
// given
PostDTO postDTO = initPostDTO();
//when
ResultActions resultActions = mockMvc.perform(post("/post")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(postDTO)));
//then
resultActions
.andExpect(jsonPath("data").exists())
.andExpect(jsonPath("data").hasJsonPath())
.andExpect(jsonPath("data.id").exists())
.andExpect(jsonPath("message").value("success"))
.andExpect(jsonPath("data.id").value(1))
.andExpect(status().isCreated())
.andDo(print());
}
@Test
@DisplayName("게시물 가져오기 테스트 - 성공")
@Order(2)
public void getPostSuccess() throws Exception {
// given
PostDTO postDTO = initPostDTO();
postService.createPost(postDTO);
// when
ResultActions perform = mockMvc.perform(get("/post/{id}", 1));
// then
perform
.andExpect(status().isOk())
.andExpect(jsonPath("message").exists())
.andExpect(jsonPath("message").value("success"))
.andExpect(jsonPath("data").exists())
.andExpect(jsonPath("data.id").exists())
.andExpect(jsonPath("data.title").exists())
.andExpect(jsonPath("data.title").value("title"))
.andExpect(jsonPath("data.contents").exists())
.andExpect(jsonPath("data.contents").value("contents"))
.andDo(print());
}
@Test
@DisplayName("존재하지않는 게시물 가져오기")
@Order(3)
public void getPostFail() throws Exception {
mockMvc.perform(get("/post/{id}", 9999999))
.andExpect(status().isNotFound())
.andExpect(jsonPath("message").exists())
.andExpect(jsonPath("message").value("error"))
.andExpect(jsonPath("errors[0]").exists())
.andExpect(jsonPath("errors[0].field").doesNotExist())
.andExpect(jsonPath("errors[0].message").exists())
.andExpect(jsonPath("errors[0].message").value("해당 포스트가 존재하지 않습니다."))
.andDo(print());
}
@Test
public void 게시물_제목_입력안함() throws Exception {
// given
PostDTO postDTO = PostDTO.builder()
.contents("contents")
.build();
//when
ResultActions resultActions = mockMvc.perform(post("/post")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(postDTO)));
//then
resultActions
.andExpect(jsonPath("errors").exists())
.andExpect(jsonPath("errors").isArray())
.andExpect(jsonPath("errors[*].field", containsInAnyOrder("title")))
.andExpect(jsonPath("errors[*].message", containsInAnyOrder("제목을 입력해주세요.")))
.andExpect(status().isBadRequest())
.andDo(print());
}
@Test
public void 게시물_내용_입력안함() throws Exception {
// given
PostDTO postDTO = PostDTO.builder()
.title("title")
.build();
//when
ResultActions resultActions = mockMvc.perform(post("/post")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(postDTO)));
//then
resultActions
.andExpect(jsonPath("errors").exists())
.andExpect(jsonPath("errors").isArray())
.andExpect(jsonPath("errors[*].field", containsInAnyOrder("contents")))
.andExpect(jsonPath("errors[*].message", containsInAnyOrder("내용을 입력해주세요.")))
.andExpect(status().isBadRequest())
.andDo(print());
}
@Test
public void 게시물_내용_제목_입력안함() throws Exception {
// given
PostDTO postDTO = PostDTO.builder()
.build();
//when
ResultActions resultActions = mockMvc.perform(post("/post")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(postDTO)));
//then
resultActions
.andExpect(jsonPath("errors").exists())
.andExpect(jsonPath("errors").isArray())
.andExpect(jsonPath("errors").hasJsonPath())
.andExpect(jsonPath("errors", hasSize(2)))
.andExpect(jsonPath("errors[*].field", containsInAnyOrder("title", "contents")))
.andExpect(jsonPath("errors[*].message", containsInAnyOrder("제목을 입력해주세요.", "내용을 입력해주세요.")))
.andExpect(status().isBadRequest())
.andDo(print());
}
}
errors[*] 문법은 errors의 원소의 순서없이 containsInAnyOrder의 값이 있냐없냐를 체크하기 위함입니다.
진행 코드는 여기서 볼 수 있습니다.