반응형

1. 과제

 테스트 코드를 작성하라.

2. 배운점

2.1. 테스트 코드

토이프로젝트에 TDD를 적용해볼까 하고 테스트코드에 대해 알아본 적이 있었다. 무작정 예제코드를 통해 공부를 시작했는데 개념과 방법론을 이해하지 못하니 어렵고 복잡하게만 느껴져 건너 뛴적이 있다.

 이번 주자에 테스트 코드를 작성하면서 개념과 방법론에 대해 다시 궁금해졌다. 사용하는 개념들을 정리하고 멘토님의 조언을 통해 TDD의 필요성을 조금은 이해하게 된것같다. 그중에서도 특히 이해가지 않던 Mock Object. 가짜객체에 대한 개념과 사용 이유를 확실히 이해하게 되었다. 이 내용은 현재 정리중이며 어느정도 정립이 되었을 때 블로그에 올리도록 하겠다.

 

2.1. given.willthrow에 대한 문제

 예외를 설정하는 stubbing 메서드인 given.willthrow를 호출했을 때 실제로 해당 예외가 발생하는 이슈가 있었다.

 원인은 동일한 상황에 대한 stubbing 케이스에 대해 given.willthrow 구문이 두번 실행될 경우 발생했다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
    ...
    
        @Nested
        @DisplayName("Task 상세조회 테스트")
        class TaskDetailTest{
            
            @BeforeEach
            void setUp(){
                ...
                
                   given(taskService.getTask(INVALID_ID)).willThrow(new TaskNotFoundException(INVALID_ID));
          }
 
            @Test
            @DisplayName("유효 ID 상세조회")
            void detailWithValidId() throws Exception {
                ...
            }
 
            @Test
            @DisplayName("유효하지 않는 ID 상세조회")
            void detailWithInvalidId() throws Exception {
 
                mockMvc.perform(get("/tasks/"+INVALID_ID))
                        .andExpect(status().isNotFound())
                        .andExpect(content().string("{\"message\":\"Task not found\"}"));
            }
        }
cs

TaskDetailTest에 대한 Junit 테스트를 실행할 경우 detailWithValidId, detailWithInvalidId 메서드에 대한 테스트를 각각 진행하게 되는데 @BeforEach 어노테이션으로 인해 한 메서드의 테스트가 시작할때마다 setUp() 메서드를 호출하게 됐고, 두번째 테스트 시 호출되는 setUp() 의 given절에서 TaskNotFoundException 이 발생하게 됐다.

다양한 테스트를 통해 실제 예외가 발생한 구간을 알아내었다.

 

1. setUp() 메서드 호출

2. given() 메서드를 통해 taskService.getTask에 대한 stubbing 객체 생성

3. willThrow를 통해 stubbing에 대한 가짜 예외 생성

4. detailWithValidId 테스트 코드 실행

5. setUp() 메서드 호출

6. given() 메서드를 통해 taskService.getTask에 대한 stubbing 객체 시 TaskNotFoundException  발생!!

 

즉, taskService.getTask에 대한 stubbing이 정해진 상태에서 다시 given() 을 통해 해당 taskService.getTask()에 대한 stubbing을 생성하려 하나 이미 TaskNotFoundException을 갖고 있기에 해당 값이 반환되고 있었다.

reset 메서드를 이용하여 taskService를 리셋시키거나, given.willthrow 구문이 한번만 실행될 수 있도록 setUp에서 detailWithInvalidId 로 이동시켜주면 된다.

 

3. 느낀점

테스트 코드를 처음 작성하며 느낀건,

첫번째, 테스트 코드는 객체 지향적이고 클린한 코드를 만들수 있도록 노력하게 해준다(?)

 단위 테스트는 객체, 메서드 단위이다. 결합도가 높거나 코드가 복잡할 경우 테스트의 범위도 커지기 때문에 다양하고 정밀한 테스트가 어려워진다. 때문에 테스트를 위해 객체지향적이고 클린한 코드를 지향하게 되는 느낌을 받았다.

 

두번째, 현업에서 TDD를 지양하는 이유

 작성한 모든 로직에 대한 테스트 코드를 작성하니 실제 로직을 작성하는 시간과 테스트 코드를 작성하는 시간이 비슷하게 들었다. 실제 서비스를 개발하는 현업에서 TDD가 포함된다면 그 공수는 배로 들지않을까.. 라는 생각과 함께 TDD를 지양하는 이유에 대해 확실히 느끼게 되었다.

 

세번째, 테스트를 코드로

 현업에서 어떠한 테스트를 한다라는 건 실제로 API를 호출해 보는 것이었다. 테스트는 로직이 개발되거나 수정됐을 때 진행했기에 코드가 수정이 될 때마다 실제 API를 호출했다. 하지만 이를 코드로 구현하니 테스트에 걸리는 시간을 많이 줄일 수 있고, 현업에서 TDD를 지양하는 이유인 '시간'이 이 상쇄될 수 있지 않을까라는 생각이 들었다. 테스트를 코드로 한다는 건 딱 들었을땐 당연한 소리같지만 실제 현업에서는 당연하지만은 않았기 때문이다. 테스트를 코드로 구현한다는 것에 다시금 많은 생각이 들었던 주차였다.

 

반응형

+ Recent posts