🎁[속보] 인프런 내 깜짝 선물 출현 중🎁

인프런 워밍업 스터디 클럽 3기 백엔드-code 4주 차 마지막 발자국

이 글은 박우빈님의 Practical-Testing 강의 를 참조하여 작성한 글 입니다.

또 다시 일주일이 지나 어느새 마지막 발자국만을 남겨두었다.

반신반의하며 시작했지만, 한 달 동안 무려 2개의 강의를 완강하고 미션까지 모두 수행한 내 자신이 참 대견하다! 히히 😊

벌써 1분기가 끝난게 믿기지 않지만, 그래도 이것저것 공부하며 알차게 살았더니 이번 1분기는 아쉽지 않게 보내줄 수 있을 것 같다.

 

특히 테스트 코드와 관련해 많은 지식을 얻을 수 있었다.

강의 내용이 알찼던 것은 물론이고, 미션을 수행하며 쌓은 지식을 정리하고 다른 사람의 코드를 읽는 과정에서도 많은 배움이 있었다.

무엇보다 우빈님께 직접 코드 리뷰를 받으면서 내 코드의 개선점을 확인하고, 작성 과정에서 궁금했던 부분을 직접 물어볼 수 있었던 경험이 정말 값졌다.

테스트 코드를 작성할 때마다 내가 올바르게 작성하고 있는 지에 대한 의심이 항상 존재하였는데, 코드 리뷰를 통해 테스트 코드 작성에 대한 자신감이 한층 더 생긴 것 같다!ㅋㅋㅋ

또한 리뷰를 통해 많은 고민 및 궁금증을 해결할 수 있었고, 가독성 및 유지보수 하기 좋은 테스트 코드 작성법 및 테스트 작성의 중요성을 한층 더 깨닫게 된 것 같다!

다음에 스터디 클럽이 또 열린다면,,무조건 참여하길 바란다👍🏻


학습 내용 요약

 

Mock을 마주하는 자세

 

더나은 테스트를 작성하기 위한 구체적 조언

  • 완벽하게 제어하기

    • 테스트 코드를 작성할 때 모든 조건들은 완벽히 제어가 가능해야 한다.

    • LocalDateTime.now()와 같이 제어할 수 없는 값은 최대한 지양하자!

       

     

  • 테스트 간 독립성을 보장하자

    • 공유 변수 사용x

     

  • 한 눈에 들어오는 Test Fixture 구성하기

    • Test Fixture

      • given절에서 생성했던 모든 객체들을 의미

      • 테스트를 위해 원하는 상태로 고정시킨 일련의 객체

    • BeforeEach, BeforeAll, AfterEach, AfterAll

      • 셋업에 유치한 이런 공통의 픽스처들은 테스트와 결합도 생기게 만듦

      • 픽스처들을 수정하거나 하는 경우에 모든 테스트에 공통으로 영향을 주기 때문에 지양하는 것이 좋음

      • 사용하는 기준:

        • 각 테스트 입장에서 봤을 때 아예 몰라도 테스트 내용을 이해하는 데 문제가 없을 때만 사용하기

        • 수정해도 모든 테스트에 영향을 주지 않는 경우에만 사용하기

    • 테스트 시 sql로 given 객체 생성하지 말자!

      • given절이 파편화되어 뭘 테스트 해야하는지 파악하기 어려워짐

      • 프로젝트가 커질수록 데이터 구조, 필드 등 변경이 발생하면 sql문 관리가 어려워짐

     

  • Test Fixture 클렌징

    • deleteAllInBatch

      • 테이블 전체를 bulk성으로 날릴 수 있는 좋은 메서드

      • 순서를 잘 고려를 해야함(중간테이블 먼저 삭제해주어야 함)

    • deleteAll

      • select후 delete하기 때문에 테이블에 있는 데이터 수 만큼 쿼리가 실행됨

      • 순서 고려x

     

  • 테스트 수행도 비용이다. 환경 통합하기

    • service와 repository부분을 하나의 추상클래스(e,g,.IntegrationTestSupport)를 상속받게 함으로 서버 실행 횟수를 줄이기!

     

  • Q. 테스트에서만 필요한 메서드가 생겼는데 프로덕션 코드에서는 필요 없다면?

     

    • 무엇을 테스트하고 있는지를 명확히 인지하기

      • 현재 동작하고 있는 프로덕션 코드를 테스트 한다면 테스트에서는 필요한데 프로젝트에서는 필요 없는 그런 메소드들이 나올 수가 있다.

      • 이런 경우 만들어도 된다. 하지만 최대한 지양 하자.

         

      • getter, 기본 생성자, 생성자 빌더, 사이즈 이런 것들 어떤 객체가 마땅히 가져도 될 마땅히 가져도 되는 행위라고 생각이 되면서 미래에도 충분히 사용될 수 있는 성격의 메소드일 것 같다.

 

Spring REST Docs

  • 테스트 코드를 통한 API 문서 자동화 도구

     

  • API 명세를 문서로 만들고 외부에 제공함으로써 협업을 원할하게 함

     

  • 기본적으로 AsciiDoc을 사용하여 문서 작성

     

     

    • 장점

      • 테스트를 통과해야 문서가 만들어짐(신뢰도 높음)

      • 프로덕션 코드에 비침투적

    • 단점

      • 코드 양이 많다

      • 설정이 어렵다

     

미션

Day 18 - @BeforeEach, given절, when절에 항목 배치

나의 코드: https://inf.run/fK9MY

 

🧐 고려한 부분

  • 남아있는 데이터가 테스트에 영향을 끼치지 않도록 각 테스트 수행 전 @BeforeEach를 통해 데이터를 초기화 시켜주었다.

  • 모든 댓글 테스트에서 필요한 사용자와 게시물은 테스트의 기본 전제 조건이므로, 사용자 생성 및 게시물 생성 메서드를 별도로 분리하여 중복을 제거하였다.

 

우빈님의 코드 리뷰 (Feat. Day 11 - 스터디 카페 이용권 선택 시스템 테스트 코드 작성하기 )

나의 코드: https://inf.run/xPthb

 

Q. 나의 질문: @CsvSource내 enum(passType) 문자열 직접 작성으로 인한 유지보수 비용 증가 문제

A. 우빈님 답변: passType이 변경되면, 이를 테스트하고 있는 부분이 같이 영향을 받게 되고

변경한 사람은 테스트 수행 시점에 테스트가 깨진 것을 보고 영향 범위를 인지할 수 있다.

따라서 테스트 코드가 없어서 영향도를 인지하지 못하는 것보다 나은 것이라 생각한다!

물론 테스트 코드를 수정해야 하는 비용은 들겠지만, 테스트 코드는 원래 프로덕션 코드와 같이 상생하는 코드이므로,

프로덕션 코드를 팔로업하면서 같이 변경되는 것이 더 자연스럽다!

@ParameterizedTest
    @CsvSource({
            "HOURLY, HOURLY, true",
            "HOURLY, WEEKLY, false"
    })
    @DisplayName("이용권 내 이용권 타입이 비교하는 타입과 같은지 비교할 수 있다.")
    void isSamePassType(StudyCafePassType passType, StudyCafePassType expectedPassType, boolean expectedResult) {
        // given
        StudyCafeSeatPass pass = StudyCafeSeatPass.of(passType, 2, 4000, 0.0);
        // when
        boolean result = pass.isSamePassType(expectedPassType);
        // then
        assertThat(result).isEqualTo(expectedResult);
    }

 

Q. 나의 질문: 일급컬렉션 내 테스트만을 위한 메서드 추가 없이 테스트 한 방법

A. 우빈님 답변: 기본적으로 프로덕션에 없는 코드를 테스트 코드만을 위해 추가하는 것은 '지양'하는 것이 맞다.

하지만 해당 기능이 단순하고, 미리에도 충분히 활용될 수 있다면 아주 보수적으로 추가해도 좋다고 생각한다.

e,g,. 단순히 일급컬렉션 내부의 원소 개수를 반환하는 size(), 내부 항복이 존재하는지 확인할 수 있는 isEmpty() 등

 

해당 테스트에서 검증하고 싶은 것은 전체 개수가 아닌 타입별 개수이므로, 타입별 개수를 반환하는 메서드를 추가하기에는 애매해 보인다.

따라서 어쩔 수 없이 아래 코드 처럼 작성하는 것이 맞으나, findPassBy()와 결합도가 생기는 것을 감안하면 될 것 같다고 하셨다.

하지만 findPassBy() 단위 테스트는 필수!!

@DisplayName("파일을 읽어 이용권 목록을 가져올 수 있다.")
    @Test
    void getSeatPasses() {
        // given
        SeatPassFileReader seatPassFileReader = new SeatPassFileReader();
        // when
        StudyCafeSeatPasses seatPasses = seatPassFileReader.getSeatPasses();
        // then
        assertAll(
                () -> assertThat(seatPasses.findPassBy(StudyCafePassType.HOURLY)).hasSize(6),
                () -> assertThat(seatPasses.findPassBy(StudyCafePassType.WEEKLY)).hasSize(5),
                () -> assertThat(seatPasses.findPassBy(StudyCafePassType.FIXED)).hasSize(2));
    }

 

아래 테스트는 변수명 잘 지었다고 받은 칭찬 리뷰이다😆

image

 

마무리

한 달동안 정말 많은 것을 배웠다,,,희희

단순히 일방적으로 인풋만 넣는게 아니라,

매일 강사님 그리고 스터디원분들과 소통하면서 함께 학습하니 더욱 단기간에 성장할 수 있었던 것 같다ㅎㅎㅎ

미션도 너무너무 재밌었고, 그에 대한 우빈님의 피드백까지 받으니 정말이지 알차고 소중한 경험이었다.

다음에 워밍업 스터디 클럽이 또 하게 된다면, 주변에 적극적으로 홍보해야겠다!!

스터디를 기획 및 운영하신 모든분들, 강사님, 그리고 스터디에 참여한 스터디 분들 모두 고생 많으셨습니다

 

댓글을 작성해보세요.


채널톡 아이콘