ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 테스트 코드가 아무리 많아도 진짜 필요한 것을 테스트하지 않는다면? (게시글 작성하기)
    Today I Learned 2022. 11. 25. 23:19

     

    게시글 작성 화면에서 내용을 입력하고 제출하기 버튼을 누르면 새로운 게시글이 등록되는 기능을 구현했다.

     

     

    폼에 종목, 날짜, 시간, 장소, 모집할 인원 수, 상세 내용을 입력하고 작성하기 버튼을 누르면 게시글이 등록된다.

     

     

    다른 사용자는 해당 게시글에 참가 신청을 할 수 있고, 작성자는 신청자 정보를 확인할 수 있다.

     

     

    API 요청으로 전달되는 날짜와 시간 값을 구분해 저장하기 위해 값 객체 GameDate를 GameDateTime으로 명칭을 변경하고, 필드를 LocalDate 타입의 date, LocalTime 타입의 startTime, endTime으로 분리했다.

     

    // GameDateTime.java
    
    @Embeddable
    public class GameDateTime {
        @Column(name = "date")
        private LocalDate date;
    
        @Column(name = "start_time")
        private LocalTime startTime;
    
        @Column(name = "end_time")
        private LocalTime endTime;
    
        private GameDateTime() {
    
        }
        
        // ...
    }

     

    게시글 작성을 위해 DTO로 전달받는 데이터 구조에 맞춰 LocalDate와 LocalTime 필드를 갖는 GameDateTime을 생성하는 로직을 CreatePostService에 정의했다.

     

     // CreatePostService.java
     
     // gameDate: 2022년 11월 25일
     // gameTime: 09,00,12,50
     
     public class CreatePostService {
     	// constructors
     
     	public CreatePostAndGameResultDto createPost(
            String gameDate,
            String gameTime,
        ) {
            // ...
            
            GameDateTime gameDateTime = createGameDateTime(
                gameDate,
                gameTime
            );
    
            // ...
        }
        
        public GameDateTime createGameDateTime(String gameDate,
                                                String gameTime) {
            String[] yearMonthDay = gameDate.split(" ");
            int year = parseDateType(yearMonthDay[0]);
            int month = parseDateType(yearMonthDay[1]);
            int day = parseDateType(yearMonthDay[2]);
    
            String[] startAndEndHourMinute = gameTime.split(",");
            int startHour = Integer.parseInt(startAndEndHourMinute[0]);
            int startMinute = Integer.parseInt(startAndEndHourMinute[1]);
            int endHour = Integer.parseInt(startAndEndHourMinute[2]);
            int endMinute = Integer.parseInt(startAndEndHourMinute[3]);
    
            return new GameDateTime(
                LocalDate.of(year, month, day),
                LocalTime.of(startHour, startMinute),
                LocalTime.of(endHour, endMinute)
            );
        }
    
        public int parseDateType(String dateType) {
            return Integer.parseInt(dateType);
        }
     }

     

    LocalDate와 LocalTime 값을 게시물 목록, 상세 내용 조회 시 출력 형태에 맞게 응답으로 반환해주기 위해 값 객체에 변환 동작을 정의했다.

     

    // GameDateTime.java
    
    public String joinDateAndTime() {
        String startHour = formatTime(startTime.getHour());
        String startMinute = formatTime(startTime.getMinute());
        String endHour = formatTime(endTime.getHour());
        String endMinute = formatTime(endTime.getMinute());
    
        return date.getYear() + "년 "
            + date.getMonthValue() + "월 "
            + date.getDayOfMonth() + "일 "
            + startHour + ":"
            + startMinute + "~"
            + endHour + ":"
            + endMinute;
    }
    
    private String formatTime(int time) {
        return time < 10
            ? "0" + time
            : Integer.toString(time);
    }

     

     

    예외처리를 제외한 모든 로직 작성을 마치고, 프론트엔드와 백엔드 테스트 코드가 모두 통과하는 상태를 확인하고 실제로 글이 등록되는지 시도해보았는데 등록이 되지 않았다.

     

    백엔드에서는 날짜 데이터가 "yyyy년 mm월 dd일"의 형태로 전달받을 것으로 상정하고 동작을 구현한 상태였다. 프론트엔드 테스트 코드에서는 단순히 change 이벤트가 발생했을 경우 핸들러 함수를 호출하는지 정도만 확인하고 있었고, 어떤 값이 전달되는지까지는 확인하지 않고 있었다. 혹시 프론트엔드에서 해당 양식대로 데이터를 전달하지 않는 것인가 싶어 테스트 코드를 수정해 UI에서 날짜를 선택했을 때 어떤 데이터를 전달하는지 확인했다.

     

     

     

    예상했던 것과 다른 형태로 날짜 데이터가 전달되는 것을 확인할 수 있었다.

     

    달력 컴포넌트에 선택된 날짜로 전달하는 selected 값과 API 요청에 전달하는 데이터로 모두 동일한 Date 객체를 사용하고 있던 것을 생각하지 않았던 데에서 발생했던 문제였다. 그렇기 때문에 프론트엔드에서 관리하는 상태의 형태를 "yyyy년 mm월 dd일" 형태로 바꿔서 관리하는 것은 낭비가 될 수 있었고, 미리 작성했던 백엔드의 날짜 변경 로직을 수정했다.

     

    // CreatePostService.java의 날짜 parsing 로직을 다음과 같이 수정
    
    // gameDate: 2022-11-25T00:00:00.000Z
    
    public GameDateTime createGameDateTime(String gameDate,
                                            String gameTime) {
        String[] yearMonthDay = gameDate.split("-");
        int year = parseDateType(yearMonthDay[0]);
        int month = parseDateType(yearMonthDay[1]);
        int day = parseDateType(yearMonthDay[2].split("T")[0]);
        
        // ...
    }

     

    코드의 오류 양이 많았던 것은 아니었지만, 모든 단위 영역의 변화를 통제하고 있다고 생각한 상황에서 의도한 대로 동작이 이루어지지 않았기 때문에 사실상 동작이 이루어지는 모든 영역을 다시 살펴보는 데 필요 이상의 시간을 써야 했다.

     

    테스트를 아무리 많이 작성해도 결국 그 단위에서 가장 중요한 동작이 무엇인지, 오류가 발생할 가능성이 큰 영역이 어디인지 생각하지 않은 채 단순히 화면에서 텍스트를 찾고, 이벤트를 일으키고, 함수를 호출하는지 검증하는 테스트를 많이, 그리고 기계적으로만 작성한다면, 테스트 코드를 작성하는 데 아무리 시간을 많이 들인다고 해도 반복적인 작업만 하고 있을 가능성이 크다.

     

    단위 테스트는 무적이 아니다.

     

     

     

     

    - 작업 링크
    https://github.com/hsjkdss228/smash-backend/pull/20

    https://github.com/hsjkdss228/smash-frontend/pull/31

     

     

     

     

    댓글

Designed by Tistory.