단위 테스트에서는 가독성
이 제일 중요하다.
실제 운영 환경에서는 효율성
이 제일 중요하지만 테스트 환경에서는 해당 사항이 별로 중요하지 않다. 따라서 단위 테스트를 작성할 때는 효율적인 코드보다는 읽기 쉬운 코드를 작성하자.
@Transactional
@Test
public void saveWritersMany() {
//given
Writer me = saveDiaryService.saveWriter("ME", "ME@NAVER.COM", Role.User);
Writer other = saveDiaryService.saveWriter("other", "OTHER@NAVER.COM", Role.User);
Writer another = saveDiaryService.saveWriter("another", "Another@NAVER.COM", Role.User);
//when
Writer foundMe = writerRepository.findAll().get(0);
Writer foundOther = writerRepository.findAll().get(1);
Writer foundAnother = writerRepository.findAll().get(2);
//then
assertThat(foundMe).isEqualTo(me);
assertThat(foundMe.getName()).isEqualTo(me.getName());
assertThat(foundMe.getEmail()).isEqualTo(me.getEmail());
assertThat(foundMe.getRole()).isEqualTo(me.getRole());
logger.info(foundMe.toString());
assertThat(foundOther).isEqualTo(other);
assertThat(foundOther.getName()).isEqualTo(other.getName());
assertThat(foundOther.getEmail()).isEqualTo(other.getEmail());
assertThat(foundOther.getRole()).isEqualTo(other.getRole());
logger.info(foundOther.toString());
assertThat(foundAnother).isEqualTo(another);
assertThat(foundAnother.getName()).isEqualTo(another.getName());
assertThat(foundAnother.getEmail()).isEqualTo(another.getEmail());
assertThat(foundAnother.getRole()).isEqualTo(another.getRole());
logger.info(foundAnother.toString());
}
잡다하고 세세한 사항으로 범벅된 코드를 좀 더 간결하고 표현력이 풍부한 코드로 리팩토링하자.
이것저것 잡다한 개념을 연속으로 테스트하는 긴 함수는 피해야 한다.
@Transactional
@Test
public void saveWritersMany() {
testSavingWriter("me", "me@NAVER.COM", Role.User, 0);
testSavingWriter("other", "OTHER@NAVER.COM", Role.User, 1);
testSavingWriter("another", "Another@NAVER.COM", Role.User, 2);
}
private void testSavingWriter(String name, String email, Role role, int savedSequence) {
//given
Writer writer = saveDiaryService.saveWriter(name, email, role);
//when
Writer found = writerRepository.findAll().get(savedSequence);
//then
assertThat(found).isEqualTo(writer);
assertThat(found.getName()).isEqualTo(writer.getName());
assertThat(found.getEmail()).isEqualTo(writer.getEmail());
assertThat(found.getRole()).isEqualTo(found.getRole());
}
testSavingWriter()
내 assert가 여러 개 있는 이유는 Writer의 equals() 기준이 id 뿐이기 때문이다.
@Test
public void deleteDiary() throws Exception {
//일지뿐만 아니라 연관된 식단, 음식도 전부 삭제하는 것이 목적이다.
//given
String url = "/api/diary/user/diabetes-diary";
List<SecurityFoodDTO> breakFast = IntStream.rangeClosed(1, 3).mapToObj(i -> new SecurityFoodDTO("breakFast" + i, i))
.collect(Collectors.toList());
List<SecurityFoodDTO> lunch = IntStream.rangeClosed(1, 3).mapToObj(i -> new SecurityFoodDTO("lunch" + i, i))
.collect(Collectors.toList());
List<SecurityFoodDTO> dinner = IntStream.rangeClosed(1, 1).mapToObj(i -> new SecurityFoodDTO("dinner" + i, i))
.collect(Collectors.toList());
SecurityDiaryPostRequestDTO dto = SecurityDiaryPostRequestDTO.builder().fastingPlasmaGlucose(100).remark("test")
.year("2021").month("12").day("22").hour("00").minute("00").second("00")
.breakFastSugar(110).lunchSugar(120).dinnerSugar(130)
.breakFastFoods(breakFast).lunchFoods(lunch).dinnerFoods(dinner).build();
mockMvc.perform(post(url).with(user(principalDetails))
.contentType(MediaType.APPLICATION_JSON_UTF8)
.content(new ObjectMapper().writeValueAsString(dto)))
.andExpect(status().isOk())
.andExpect(jsonPath("$.success").value("true"))
.andExpect(jsonPath("$.response.id").value(1));
DiabetesDiary found = diaryRepository.findAll().get(0);
//when and then
String deleteUrl = "/api/diary/user/diabetes-diary/" + found.getId();
mockMvc.perform(delete(deleteUrl).with(user(principalDetails)))
.andDo(print());
List<DiabetesDiary> diaries = diaryRepository.findAll();
List<Diet> dietList = dietRepository.findAll();
List<Food> foodList = foodRepository.findAll();
assertThat(diaries.size()).isEqualTo(0);
assertThat(dietList.size()).isEqualTo(0);
assertThat(foodList.size()).isEqualTo(0);
}
메서드의 추상화가 제대로 이루어져 있지 않아 가독성이 매우 나쁜 코드다.
@Test
public void deleteDiary() throws Exception {
//일지뿐만 아니라 연관된 식단, 음식도 전부 삭제하는 것이 목적이다.
//given
String url = "/api/diary/user/diabetes-diary";
SecurityDiaryPostRequestDTO dto = makeDtoForDelete();
postDiaryForDelete(url, dto);
//when and then
deleteRequest();
deleteRequestIsValid();
}
private SecurityDiaryPostRequestDTO makeDtoForDelete() {
List<SecurityFoodDTO> breakFast = IntStream.rangeClosed(1, 3).mapToObj(i -> new SecurityFoodDTO("breakFast" + i, i))
.collect(Collectors.toList());
List<SecurityFoodDTO> lunch = IntStream.rangeClosed(1, 3).mapToObj(i -> new SecurityFoodDTO("lunch" + i, i))
.collect(Collectors.toList());
List<SecurityFoodDTO> dinner = IntStream.rangeClosed(1, 1).mapToObj(i -> new SecurityFoodDTO("dinner" + i, i))
.collect(Collectors.toList());
return SecurityDiaryPostRequestDTO.builder().fastingPlasmaGlucose(100).remark("test")
.year("2021").month("12").day("22").hour("00").minute("00").second("00")
.breakFastSugar(110).lunchSugar(120).dinnerSugar(130)
.breakFastFoods(breakFast).lunchFoods(lunch).dinnerFoods(dinner).build();
}
private void postDiaryForDelete(String url, SecurityDiaryPostRequestDTO dto) throws Exception {
mockMvc.perform(post(url).with(user(principalDetails))
.contentType(MediaType.APPLICATION_JSON_UTF8)
.content(new ObjectMapper().writeValueAsString(dto)))
.andExpect(status().isOk())
.andExpect(jsonPath("$.success").value("true"))
.andExpect(jsonPath("$.response.id").value(1));
}
private void deleteRequest() throws Exception {
DiabetesDiary found = diaryRepository.findAll().get(0);
String deleteUrl = "/api/diary/user/diabetes-diary/" + found.getId();
mockMvc.perform(delete(deleteUrl).with(user(principalDetails)))
.andDo(print());
}
private void deleteRequestIsValid() {
List<DiabetesDiary> diaries = diaryRepository.findAll();
List<Diet> dietList = dietRepository.findAll();
List<Food> foodList = foodRepository.findAll();
assertThat(diaries.size()).isEqualTo(0);
assertThat(dietList.size()).isEqualTo(0);
assertThat(foodList.size()).isEqualTo(0);
}
@Test
적용중인 코드를 추상화했다. 그리고 가독성이 좋게 메서드 명을 구체적으로 적었다.
또한, 위에서 아래로 자연스럽게 읽히도록 메서드를 배치하였다.