[인프런 워밍업 스터디 클럽 2기 백엔드] 4주차 + 회고

 

해당 글은 인프런 박우빈 강사님의 Practical Testing: 실용적인 테스트 가이드을 바탕으로 작성하였습니다.

 


 

강의 요약

 

Presentation Layer

  • 외부 세계의 요청을 가장 먼저 받는 계층

  • 사용자 입력 값, 프론트엔드에서 주는 값, 요청 등...

  • 주요 로직은 없지만, 최소한의 유효성 검증을 수행한다.

 

레이어드 아키텍쳐의 단점

  • 도메인에 강하게 의존

    • 도메인에 레포지토리를 위한 어노테이션을 필연적으로 붙여야 하는 문제 발생


      ⇒ 대안: 헥사고날 아키텍쳐

 

Mockito로 stubbing 하기

  • 가짜 객체를 만들어 행위를 지정하고 테스트하는 방법 (자세한 내용은 미션 6 참조)

 

Test Double

  • dummy: 아무것도 하지 않는 깡통 객체

  • fake: 단순 형태로, 동일 기능을 수행. 프로덕션에서 쓰기에는 부족한 객체

  • stub: 테스트에서 요청한 것에 대해 기록하여 보여줄 수 있는 객체. 정의하지 않은 요청에는 무응답

  • spy: stub이면서, 호출된 내용을 기록하여 보여줄 수 있는 객체. 일부는 실제 객체처럼 동작.

  • mock: 행위에 대한 기대를 명세하고, 그에 따라 동작하도록 만들어진 객체

 

stub vs mock

  • stub은 상태를 검증

  • mock은 행위를 검증

 

더 나은 테스트 작성하기

  • 한 문단에는 한 가지 주제만!

    • '테스트 코드 = 문서' 라는 것을 항상 인지해야 한다.

  • 완벽한 제어

    • 시간, 랜덤 등은 외부에서 주입받도록 리팩토링 하기. 강의 예제의 가게의 영업시간을 테스트했던 것을 기억하자.

    • 외부 세계와 소통해야 하는 것은 mocking을 이용한다.

  • 독립성 보장하기

    • 한 테스트 메서드에는 한 가지 기능만 테스트하기

    • given에서는 가급적 순수 생성자 또는 빌더를 통해 생성하기

    • 검증이 있는 팩토리 메서드는 사용 시 예외 발생 가능성이 있다.

    • 테스트는 순서 보장이 안 되어 있다.

  • Fixture 구성

    • @BeforeEach, @BeforeAll

      • 중복 코드를 줄일 수 있지만 테스트 간의 결합이 생겨버린다.

      • 신중한 사용이 필요!

  • Fixture 클렌징

    • deleteAll vs deleteAllInBatch

      • deleteAllInBatch(): 중간 테이블에 외래키 등으로 인한 오류 발생 때문에 데이터 삭제 순서를 고려해야 함. → 코치님의 추천 메서드

      • deleteAll(): 순서 고려 필요가 없고 연관 테이블을 자동적으로 같이 지워주지만, 삭제 쿼리가 복잡하게 나간다! 전체 테이블을 조회하고 건별로 삭제하는 방식이기에 불필요한 쿼리가 늘어난다.

  • @ParameterizedTest

    • 케이스 확장할 때 좋은 방식!

    • 재고 타입 테스트할 때를 기억하기!

    • @CsvSource로 제공된 데이터를 @ParameterizedTest가 붙은 메서드의 파라미터에 각각 제공 가능

    • @CsvSource 외에도 @MethodSource, @ValueSource 등이 존재

  • @DynamicTest

    • 일련의 시나리오를 테스트

    • @TestFactory가 붙은 메서드에서 Iterable한 반환 값을 던지는 형태

    • 공통의 환경에서부터 일련의 사건을 실행시켜 테스트가 가능.

    • given/when/then 세트가 복수로 생기기 때문에 가독성 저하에 주의하기.

  • 환경 통합하기

    • gradle 탭의 tesk에서 test를 수행하면 전체 테스트를 수행해보기

    • 전체 테스트 시 Spring Boot가 여러번 로딩하게 되면 비용과 시간 증가...!

       

    • 서버가 발생하는 횟수를 줄여야 한다!

      • 서버를 띄우는 @SpringBootTest과 profile을 지정하는 @ActiveProfile을 별도의 상위 클래스에 붙여 Test 클래스들에 상속시키기!

      • mock도 별도의 환경으로 취급하기 때문에 mock 객체는 상위 클래스에서 protected로 생성하거나 mock이 필요한 환경을 별도로 설정하기.

      • 환경 통합을 위해, @DataJpaTest보단 @SpringBootTest를 사용하기

  • private 메서드를 테스트하기

    • 결론적으로는, 할 필요도 없고 하려고 해서도 안된다!

    • 그래도 고민된다면, 객체를 분리할 시점인지 관해 생하기

      • 분리가 필요하다고 판단된다면 객체를 별도로 분리해 private 메서드를 공개 메서드로 작성하면 된다.

    • private 메서드는 외부에서 공개하고 싶지 않은 것이고 외부에서는 몰라도 되는 부분이다! public 메서드를 검증하다보면 자연스레 검증되는 부분이다!

+ α

  • 헥사고날 아키텍쳐

    • 도메인 주도의 아키텍쳐

  • QueryDSL

    • JPA 사용 시 거의 필수

    • 타입 체크, 동적 쿼리(값에 null이 들어올 시 자동으로 동적으로 조회해줌)가 가능

  • 낙관적 락, 비관적 락

  • CQRS(Command and Query Responsibility Segregation)

    • DB에서 조회와 업데이트를 분리

 


 

미션

미션 5

미션 6

미션 5, 6은 직접 테스트 코드를 작성했던 네 번째 미션 보다는 훨씬 편안한 내용이었다. 강의 내에서 이미 정답을 다 알려준 느낌이고, 내 나름의 언어로 정리해보는 느낌이었다.

쉽다고 만만히 보았기 때문일까... 특강 때 공통 피드백을 받고 바로 아차 싶었다. @BeforeEach 부분은 단순 중복 제거를 위한 것이 아니라는 점을 간과했다. 리팩토링 강의에서부터 가장 강조하신 것 중 하나가 도메인인데 그것을 놓치다니...🤣🤣 정답은 맞췄어도 풀이 과정 때문에 반타작 밖에 못한 기분이었다.

 


 

후기

벌써 4주차, 마지막 발자국이자 워밍업 클럽 2기의 마무리이다. 한 달이라는 시간이 쏜살같이 지나가버렸다. 테스트 코드와 리팩토링에 대해 심도 있게 다가간 것은 처음이었던 나에게는 정말 어려웠다. 강의에서 들었던 내용을 과제에 적용하는 과정은 험난했다. 과제를 모두 제출하고 완주를 한 것 만으로도 만족해버리고 말았다.

 

당장 다음주 시작부터, 새로운 스터디가 시작된다. 같이 워밍업 클럽에 참여하신 분의 자바 스터디인데, 기초부터 다시 다잡는다는 느낌이라 굉장히 기대가 된다. 👏👏

마지막으로, 이런 자리를 마련해주신 인프런과 코치님, 그리고 내 부족한 미션 피드백을 해주신 다른 스터디 참여자 분께 감사의 말씀을 드리고 싶다. 언젠가 3기가 열린다면, 다시 한 번 도전하고 싶다!

채널톡 아이콘