본문 바로가기
공부 기록

[NEXTSTEP] ATDD 과정 1주차 피드백, 2주차 시작

by 타태 2023. 7. 9.

2023.07.01 - [공부 기록] - [NEXTSTEP] ATDD 과정 1주차 시작

 

[NEXTSTEP] ATDD 과정 1주차 시작

https://edu.nextstep.camp/c/R89PYi5H ATDD, 클린 코드 with Spring edu.nextstep.camp 2023-06-26(월) ~ 2023-08-09(수) 의 기간 동안 넥스트스텝에서 진행하는 인수테스트 수업을 수강한다. 이번 기수가 7기인 만큼 해당 과

ktae23.tistory.com

 

효율적인 리뷰 프로세스

  • PR코멘트를 통해 피드백 받고 싶은 방향성에 대해 리뷰어에게 공유하기
    • 미션하면서 중점적으로 고민했던 부분이나 궁금했던 주제에 대해 리뷰어에게 알리기
    • 보다 효과적인 피드백을 받는데 도움이 됨
  • PR코멘트에 긴 질문보다는 코드 코멘트로 질문 남기기
    • 긴 질문은 리뷰어에게 심리적인 부담으로 작용
    • 효과적인 소통을 위해 코드에 바로 코멘트 작성하기
  • 피드백 코멘트로 소통하기
    • 리뷰어가 남긴 피드백에 의견을 나눌 땐 피드백 스레드를 이용하기
      • DM이나 질문 채널을 이요해도 좋음
      • 미션 피드백과 연계된 내용이라면 스레드가 효과적
      • 질문 답변 등 한 스레드로 이력 관리가 가능하여 흐름 파악이 쉬움
    • 리뷰어가 노티를 받지 못 했을 수 있으니 DM을 추가로 남기기

1주차 피드백 

  • 인수 테스트 격리하기
    • @Transactional
      • RANDOM_PORT나 DEFINED_PORT를 사용 할 경우 실제 서블릿 환경이 제공 됨
      • HTTO Client와 Server는 다른 스레드에서 동작
      • 따라서 @Transactional을 통한 롤백은 동작하지 않음
    • @DirtiesContext
      • 효과적인 테스트 수행을 위해 스프링에서는 context caching 기능 지원
      • context caching의 조건이 빈이 오염(변경) 되지 않는 경우임을 이용해서 일부러 컨텍스트를 오염시켜서 캐시 기능을 사용하지 않도록 하는 설정
      • 클래스, 메서드 단위로 설정 가능
      • 매번 컨텍스트를 새로 구성해야 해서 오래 걸림
    • @Sql 또는 쿼리 수행
      • 테스트가 수행 될 때마다 테이블을 truncate 시키는 쿼리 수행
      • 컨텍스트를 다시 띄우는 것보다는 낮은 비용
      • 테이블이 추가, 수정 될 때마다 쿼리 수정 필요
    • 코드 상으로 테이블 truncate
      • JPA 사용시 EntityManager를 이용하여 테이블 이름 조회
      • 아닌 경우 DataSource를 이용하여 테이블 이름 조회
      • 각 테이블을 truncate 시켜주는 쿼리 수행
      • 테이블 상태에 의존하지 않는 초기화 환경 구축 가능
    • 테스트가 의도치 않게 실패하여 afterEacth가 실행되지 않으면 이후 테스트가 그 상태를 바탕으로 실행 될 수 있음. 때문에 데이터를 비워주는 작업은 테스트 실행 전에 실행시켜주는 편이 좋을 수 있다.

 

인수 테스트 리팩터링

  • 테스트의 의도를 명확히 드러내기
    • 시나리오의 흐름이 잘 보이게끔 노력해야 한다.
    • 메서드로 분리하여 읽기 좋게 만들자.
  • 테스트 가독성이 중요한 이유
    • 변경 사항에 대해서 수정하기 어렵다. -> 방치될 가능성 높다
      • @Ignore or @Disabled
    • 가독성이 좋으면 해당 기능의 스펙을 나타낼 수 있다. 프로덕션 코드의 가독성이 중요한 만큼 테스트 코드의 가독성도 중요함
  • 큐컴버를 이용한 시나리오 기반 테스트
  • 테스트 코드 중복 제거
    • 기능 개발 간 테스트 코드도 중복이 많이 발생 함
    • 테스트 가독성을 저해하는 구조가 나올 수 있어 중복 제거가 중요함
    • 가독성이 좋지 않은 테스트코드는 관리가 되지 않는 가능성이 높음
  • 중복 제거 방법
    • 메서드 분리
      • 테스트 클래스 내 반복되는 코드 분리
      • 스텝 메서드들을 static 선언
      • 스텝 메서드를 모아 놓은 별도의클래스로 분리
      • 의미를 드러내기 위해 메서드 래핑하기
    • CRUD 추상화
    • Cucumber나 JBehave와 같은 BDD 도구 사용
  • 질문 / 답변
    • 구간 생성 시 응답 코드는 어떻게 해야 할까요?
    • 요구 사항에는 Location이 없는데 비워야 할까요?
      • 우리가 흔히 HTTP 기반의 API의 설계를 할 때 사용하는 규칙을 생각해보자.
      • RFC 명세를 기반으로 하는 것이 좋다고 생각 함
      • 진리의 회바회 팀바팀, 현재 상황에서 엔지니어링을 한다는 관점에서 팀과의 약속이 더 중요 함
    • 인수 시나리오 Step에는 포함되지 않지만 테스트를 하면서 요청이나 응답을 통해 값을 추출해야 할 필요가 있는 경우 어디에서 값을 가져오는게 좋을까요?
      • 정답은 아니지만 시행착오를 겪어가며 배운 강사와 리뷰어들의 제안을 받아보자
      • 테스트 내부에서 응답을 갖고 있다가 재사용하는 경우가 많았고 재사용 가능이 높은 경우 분리해서 컨텍스트 세팅용으로 사용하기도 함
    • LineService에서 Station을 조회하기 위해 참조하는 객체는 StationSercie vs StaionRepository
      • 설계에 따라 다름..;;; 핫한 논쟁
    • fixture 메서드 명에는 어던 걸 봔환해야 할지 명시해야 할까 리뷰 받기 이전에는 인수 테스트를 확인하는 사람이 해당 애플리케이션 개발자라는 관점에서 작성 했기 때문에 fixture에서 ExtractableResponse<Response>를 반환하고 테스트 메서드에서는 그 반환값을 정제해서 테스트를 수행했습니다. 테스트 메서드 이름에 반환값까지 적어줘야 할지
      • 메서드 시그니처 만으로 나타내도록 노력하는게 좋지만 응답의 결과를 뽑는 메서드가 여러 개 필요 할 경우를 조심하자
    • 인수 테스트의 경우 QA 팀에서 작성하는 경우가 많은데 개발쪽에서 작성하면 역할이 겹치지 않을까 하는 생각이 들었다.
      • QA분들이 챙기긴 하겠지만 개발자가 더 잘 챙길 수 있는 회색지대가 있을 수 있기 때문에 겹친다기 보다 같이 이해를 공유하기 위해 진행한다고 생각해보는 것이 좋다고 생각 한다. 개발자와 QA가 바라보는 중요한 점이 다를 수 있다.

 


 

2주차 미션 설명

  • 미션 준비 방법
    • 지난 주차 미션의 코드 사용하기
      • 지난 주차 미션에서 구현한 코드를 가져옵니다.
      • 인텔리제이를 통해 쉽게 코드 가져오기
      • 지난 주차 미션에서 구현했던 코드를 옮겨옵니다.
      • 만약 아직 미션 수행이 끝나지 않았다면 defalut-sample 브랜치를 참고하여 코드를 옮겨 옵니다.
    • 새로운 뼈대 코드를 사용하기
      • 아직 미션이 끝나지 않았거나 재사용하기 어려운 경우 샘플 코드로 시작할 수 있습니다.
      • default-sample 브랜치에서 샘플 뼈대 코드를 확인할 수 있습니다.

단위 테스트

  • 협력 객체에 다른 구분
    • 통합과 고립(Sociable and Solitary)
      • 협력 객체를 실제 객체로 사용하는지 Mock 객체를 사용하는지에 따라 테스트 구현이 달라짐
      • Stub
        • 협력 객체의 상태를 지정해 줌
  • 작은 코드 조각(단위)를 검증
  • 빠르게 수행 가능
  • 격리된 방식으로 처리
  • 테스트 주도 개발
    • Test Double
      • 실제 객체 대신 사용되는 모든 종류의 객체에 대한 일반 용어
      • Dummy Object
      • Test Spy
        • 객체의 특정 행위를 미리 지정해 둔 객체
      • Test Stub
        • 테스트 대상의 상태를 테스트에서 설정 함
        • Mockito 활용
          • Mockito 라이브러리를 사용했다고 하더라도 무조건 Mock 객체인 것은 아님, 활용 방식에 따라 다르게 사용 될 수 있음
        • MockitoExtension.class 를 Junit @ExtendWith에 넣어 사용할 수 있음
          • @Mock은 그냥 일반 가짜 객체라면 @MockBean을 사용하면 스프링 컨텍스트를 활용 하여 Stubbing을 하게 됨
        • 미리 행위를 설정 해 둠 - 약속한 결과를 응답하도록 하여 상태를 검증 함
      • Fake Object
        • 실제 객체가 있지만 Fake 객체를 따로 만들어서 호출을 가로채도록
        • 개발자가 직접 Fake 객체를 개발하고 동작도 지정하여 사용 한다
      • Mock Object
        • 동작을 검증하는 방식
        • 호출 횟수, 인자, 동작 등
    • final 로 선언 된 메서드 테스트는 모킹을 못할 때 어떻게 할까요?
      • 추후 공유

 

728x90

통합 VS 고립

  • 협력 객체를 실제 객체로? 가짜 객체로?
    • 실제 객체를 사용 하면 협력 객체의 행위를 협력 객체 스스로가 정의
      • 실제 객체를 사용 할 경우 협력 객체의 상세 구현에 대해서 알 필요가 없음
      • 하지만 협력 객체의 정상 동작 여부에 영향을 받음
    • 가짜 객체를 사용 하면 협력 객체의 행위를 테스트가 정의
      • 테스트 대상을 검증할 때 외부 요인(협력 객체)으로 부터 철저히 격리
      • 하지만 테스트가 협력 객체의 상세 구현을 알아야 함
  • 테스트 코드를 작성할 때
    • 가짜 객체를 활용하면 실제 객체를 사용할 때 보다 조금 더 편하게 테스트 를 작성할 수 있음
    • 하지만 상세 구현에 의존하는 테스트가 될 수 있음
  • 추천하는 방법
    • TDD를 연습할 때는 가급적이면 실제 객체를 활용하는 것을 우선으로 진행
  • 테스트 작성이 어렵거나 흐름이 잘 이어지지 않는다면 테스트 더블을 활용 하는 방법으로 접근하시는 것을 추천

Classist vs Mockist

  • 격리 된 방식으로 처리한다는 부분에서 Classist 는 테스트와 테스트 간의 격리라고 생각하고 Mockist는 테스트 대상과 협력 객체 간의 격리라고 생각한다.
  • Classist의 단위 테스트
    • 테스트를 격리해야 하는 대상은 코드가 아닌 또 다른 테스트
    • 테스트 간 공유하는 의존성이 아니라면 실제 객체 사용
    • Inside Out (or Middle Out)
      • 실제 객체를 다뤄야하기 때문에 도메인 모델로부터 시작함
      • 의존하는 협력 객체가 실제 존재해야 테스트를 작성 할 수 있음
      • 도메인 설계가 충분히 이루어진 다음 진행 가능
      • TDD 사이클을 이어나가기가 상대적으로 어려움
      • 프로덕션 코드에 덜 의존적인 테스트가 작성됨
  • Mockist의 단위 테스트
    • 테스트 대상을 협력 객체로부터 격리하기 위해 테스트 대상이 의존하는 모든 것을 가짜 객체로 대체
    • 단위와 단위의 통합이 잘 동작하는지 검증하는 것
    • Outside In
      • 상위 레벨 테스트부터 시작
      • 테스트 더블을 활용하여 테스트 대상이 의존하는 협력 객체의 예상 결과를 정의
      • 다음 사이클로 테스트 더블로 미리 정의한 협력 객체를 테스트 대상으로 함
      • OO와 TDD가 익숙하지 않은 사람들에게 가이드할 때 도움이 된다고 생각됨
      • 도메인에 대한 이해도가 높지 않은 상태에서 진행이 가능
      • 상대적으로 프로덕션 코드에 의존적인 테스트가 작성됨 (깨지기 쉬운 테스트)
  • 아는 것에모르는 것으로
    • Test-Driven Development, kent beck
  • 사실은 상향식, 하향식 둘 다 TDD의 프로세스를 효과적으로 설명해 줄 수 없 다. ... 만약 어떤 방향성을 가질 필요가 있다면 '아는 것에서 모르는 것으로 (known-to-unknown)' 방향이 유용할 것이다. 우리가 어느 정도의 지식과 경험을 가지고 시작한다는 점, 개발하는 중에 새로운 것을 배우게 될 것임을 예 상한다는 점 등을 암시한다.
  • 추천하는 방법
    • Top-Down으로 방향을 잡고, Bottom-Up으로 구현하기
    • 인수 테스트 작성을 통해 요구사항과 기능 전반에 대한 이해를 선행
    • 내부 구현에 대한 설계 흐름을 구상
    • 설계가 끝나면 도메인부터 차근차근 TDD로 기능 구현
    • 만약 도메인이 복잡하거나 설계가 어려울 경우 이해하고 있는 부분 부터 기 능 구현

 

반응형

댓글