3주차 Association

0. 배운 사항들

  • 인수테스트란?

    • 사용자에게 애플리케이션을 인도할 수 있는지 검증하는 절차 (기능 구현 확인)

      • 사용자 스토리를 검증하는 기능 테스트

      • 사용자 스토리로 테스트할 시나리오 지정

    • 이번 과정에서는 API 접점에서 검증하는 E2E 테스트입니다.

  • 인수테스트를 하는 이유

  • 테스트의 종류

    • 단위 테스트 - 구현한 부분, 단위를 검증

    • 통합 테스트 - 각 단위들이 유기적으로 잘 동작하는지 검증하는 테스트

    • E2E 테스트 - 요청과 응답 기준으로 API를 종단하는 테스트

    • API 테스트

    • 시나리오 테스트

  • RestAssured를 이용한 인수테스트 작성

1. [실습] 구현단계들

1단계 - OneToMany (FetchType.Eager)

2단계 - Lazy Loading By Proxy

3단계 - One To Many (FetchType.Lazy)

2. 배운 사항

A. 요구사항 분석 및 도출

1. 기능 요구사항

  • 기능/ 프로그래밍/ API 명세로 나눠서 요구사항을 받았을 때, 계층 별로 도메인 로직, 코드레벨, 인터페이스 로직들을 정확히 어떻게 구현해야 될지 알 수 있었습니다.

  • 테스트되어야 할 기능 목록들을 의미하며 도메인 로직들을 이야기합니다.

  • 요구사항에서 인수조건들(Given, When, Then)을 도출합니다.

2. 프로그래밍 요구사항

  • 재사용성, 가독성, 테스트의 격리 등 테스트에 대한 원칙들을 지키면서 구현하기

  • 패키지 구조, 의존성 관리 등 보이지 않는 규칙을 세워서 지키면서 구현하기

  • 객체지향적인 코드(객체지향 생활체조)라는 원칙들로 코드에서 bad smell이 나지 않게끔 구현하기

3. API 명세

  • DTO와 컨트롤러 레벨에서 어떤 값을 받고 어떤 값을 반환하는지 알 수 있었습니다

    • Controller

      • @PathVariable

      • @RequestBody

      • @GetMapping

      • HTTP 응답 코드

    • DTO

      • Request 형태

      • Response 형태

      • 가변과 불변 DTO 장단점

      • DTO에서 Entity를 의존하지만 그 반대는 좋지 않은 의존성

B. Spring 테스트

1. SpringootTest

  • Context를 테스트마다 bean들을 주입해서 생성합니다.

  • 에노테이션들

    • 환경에 대한 설정

      • @SpringBootTest - 클래스, 메서드에 명시(Context 지정)

        • @LocalServerPort - 컨텍스트의 지정 포트

        • @DirtiesContext - 클래스(매 테스트마다 초기화(시점변경가능))

    • 편리성을 위한 애노테이션

      • @BeforeEach - 테스트 실행 마다 필요한 이전 세팅들

    • 가독성을 위한 애노테이션

      • @DisplayName - 테스트 실행 시 나타나는 이름으로 명시됨

        • @Nested - 테스트간의 그룹관계를 나타냄 (예: 오류 발생시, 성공시나리오)

    • 테스트 명시

      • @Test

2. restAssured

  • RestAssured의 사용법을 익혔습니다.

 ExtractableResponse<Response> response =
            RestAssured
                .given()
                    .log().all()  // Request에 대한 로그 레벨 설정
                    .body(params) // Request의 형태
                    .contentType(MediaType.APPLICATION_JSON_VALUE)
                .when()
                    .delete(String.format("/stations/%s",id))
                    .post("/stations") // request로 대상 경로 와 http 메서드 지정
                .then()
                    .log().all()
                .extract();
                

3. JSON Path - DSL && AssertJ

  • JsonPath를 사용해서 response를 추출하였습니다.

responseStations
    .jsonPath()
        .getList(".").stream().count() // 갯수 추출
  • AssertJ 같은 메서드를 사용하였습니다.

    • assertThat()

    • doesNotContain()

    • containExactly()

assertThat(response.statusCode()).isEqualTo(HttpStatus.OK.value());
assertThat(response.jsonPath().getString("name")).isEqualTo(신분당선);
assertThat(response.jsonPath().getString("color")).isEqualTo(BG_RED_600);
assertThat(response.jsonPath().getList("stations.name")).contains(첫째지하철역1, 두번째지하철역1);

4. 가독성을 위한 꿀팁들

  • 용어의 통일성

  • Step으로 분리하기

  • 매직넘버 선언하기

  • formatter 잘 사용하기

C. 객체 맵핑

  • JPA 일급콜렉션 사용하기

  • db 스키마와 객체 맵핑

  • 패키지 구조

D. 패키지 구조

  • CQRS

  • 기능 단위 분리

  • 도메인 단위 분리

3. 구현 사항들

  • 1단계 - 지하철 인수 테스트 (기능 스펙이 존재할 때)

  • 지하철역 인수 테스트 작성

  • PR: https://github.com/next-step/atdd-subway-map/pull/813

    • 주요 피드백 사항

      • 인수 테스트 가독성

      • 주석 부분에 대한 오류

      • Format 잘 안되어 있음

  • 2단계 - 지하철 노선 관리 (인수 조건이 존재 할 때)

  • 지하철 CRUD

  • PR: https://github.com/next-step/atdd-subway-map/pull/866

    • 주요 피드백사항

      • dto는 어느 layer에서든 사용 가능하게 구현하는 부분이 맞다

        • DTO는 가변으로 사용한다.

      • 도메인에서 Setter 같은 객체 메시지를 받는 interface 만들기

      • validation도 있으면 좋다

      • 인수 테스트 가독성

  • 3단계 - 지하철 구간 관리 (기능 스펙만 존재할때)

  • 지하철 노선에 구간을 등록하는 기능을 구현 (조건 + 에러 처리)

  • PR: https://github.com/next-step/atdd-subway-map/pull/961

    • 주요 피드백사항:

      • 기획자와 QA등을 위한 '에러를 표기한다' 를 구체적인 표현으로 표기

      • assertThat(stations).containsExactly(두번째지하철역이름, 첫째지하철역이름, 세번째지하철역이름); 같이 조금 더 구체적인 테스트 검증

      • 개행 컨벤션 및 주석 삭제

      • FirstAddSection 의 잘못된 사용을 우려해서 AddSection으로 리팩토링

Last updated