본문 바로가기
~ 2024.03/Java Spring 개발

[Java Spring 개발] 테스트코드 작성

by Monett 2023. 6. 15.
반응형

패캠 강의를 보며 일단 따라쳤지만, 제대로 이해하지 않으면 나중에 이상하게 사용할 것 같아 정리

 

  • 요즘 단위테스트는 given-when-then 패턴으로 작성하는 추세이다 (참고)
    • given : 어떤 데이터가 준비되었을 때
    • when : 어떤 함수를 실행하면
    • then : 어떤 결과가 나와야 한다

 

// TodoServiceTest.java
@Test
void add() {
    when(this.todoRepository.save(any(TodoEntity.class)))
            .then(AdditionalAnswers.returnsFirstArg());

    TodoRequest expected = new TodoRequest();
    expected.setTitle("Test Title");

    TodoEntity actual = this.todoService.add(expected);

    assertEquals(expected.getTitle(), actual.getTitle());
}
  • given
    • when : todoRepository.save 함수를 실행하면
    • then : 받은 첫번째 인자를 바로 반환 (mockito 라이브러리)
    • 이후 다시 given : "Test Title"이라는 title을 가진 expected 인스턴스가 준비되었을때
  • when : todoService.add(expected) 함수를 실행하면
    • add -> todoRepository.save로 작용, 인자 그대로 반환하며 actual에 expected와 동일한 데이터 담김
  • then(assertEquals) : expected와 actual의 title이 동일

// TodoServiceTest.java
@Test
void searchById() {
    TodoEntity entity = new TodoEntity();
    entity.setId(123L);
    entity.setTitle("TITLE");
    entity.setOrder(0L);
    entity.setCompleted(false);
    Optional<TodoEntity> optional = Optional.of(entity);

    given(this.todoRepository.findById(anyLong()))
            .willReturn(optional);

    TodoEntity actual = this.todoService.searchById(123L);

    TodoEntity expected = optional.get();

    assertEquals(expected.getId(), actual.getId());
    assertEquals(expected.getTitle(), actual.getTitle());
    assertEquals(expected.getOrder(), actual.getOrder());
    assertEquals(expected.getCompleted(), actual.getCompleted());
}
  • Optional은 null 참조를 사용하는 대신 안전한 대안을 제공하도록 설계된 Java 8 클래스 (ChatGPT의 답변..)
  • given
    • anyLong으로 todoRepository.findById 함수를 호출하면 optional 인스턴스가 return됨
    • expected는 optional.get
  • when : todoService.searchById를 호출하면
    • todoService.searchById -> todoRepository.findById 적용되면서 actual에 optional과 같은 데이터
  • then: id, title, order, completed가 모두 같아야함
  • 지금 보니 actual이 expected 아래에 있는게 given-when-then 순서에 조금 더 가까운 것 같다

// TodoServiceTest.java
@Test
public void searchByIdFailed(){
    given(this.todoRepository.findById(anyLong()))
            .willReturn(Optional.empty());

    assertThrows(ResponseStatusException.class, () -> {
        this.todoService.searchById(123L);
    });
}
  • todoRepository.findById가 항상 empty를 return
  • todoService.searchById -> todoRepository.findById로 작동하며 empty return. NOT_FOUND 에러 발생하며 assertThrows 성공

// TodoControllerTest.java
@Test
void create() throws Exception{
    when(this.todoService.add(any(TodoRequest.class)))
            .then((i) -> {
                TodoRequest request = i.getArgument(0, TodoRequest.class);
                return new TodoEntity(
                        this.expected.getId(),
                        request.getTitle(),
                        this.expected.getOrder(),
                        this.expected.getCompleted());
            });
    TodoRequest request = new TodoRequest();
    request.setTitle("ANY TITLE");

    ObjectMapper mapper = new ObjectMapper();
    String content = mapper.writeValueAsString(request);

    this.mvc.perform(post("/").contentType(MediaType.APPLICATION_JSON).content(content))
            .andExpect(status().isOk())
            .andExpect(jsonPath("$.title").value("ANY TITLE"));

}
  • todoService.add 호출 시 그대로 반환
  • ObjectMapper와 mvc.perform 사용해서 post요청 전송 -> Ok와 title이 ANY TITLE이면 성공

Service쪽 정리하면서 개념이 이해됐다.

ControllerTest는 정말 이해하기 어려웠는데 지금은 어느정도 이해가 간다.

TDD를 습관화하자 ㅎㅎ

반응형