본문으로 바로가기

5주차 - 인수 테스트 기반 TDD

하루에 하나씩 미션 정리를 끝내려고 했지만,,, 그간 너무 바빠서 밀린 미션을 다시 끄적여본다,,
5주차 미션은 앞서배운 TDD를 인수 테스트 기반으로 개발해보는 것! TDD 싸이클 밖에 인수테스트 싸이클이 하나 추가된 것이다.

인수 테스트 역시 하나의 기술이 아니라 개발 방법론인 것 같다. 내가 이해한 인수 테스트는 도메인 하나하나의 기능 단위가 아니라 사용자 입장에서의 하나의 기능을 테스트코드를 작성하는 것이다. 음.. TDD 단위 테스트로 도메인 하나를 완성 하는 것이 아닌 컨트롤러부터 기능 전체적인 코드를 테스트하는 것. 그래서 먼저 실패하는 인수테스트 코드를 작성하고 TDD 단위 테스트로 도메인을 개발하고 모두 통과하면 인수테스트도 통과를 한다.
TDD 개발 방식은 안쪽에서 바깥쪽으로 개발을하는 Inside out 방식과 바깥쪽에서 안쪽으로 개발을 하는 Outside in 방식이 있다.
두 가지 각각의 차이는 컨트롤러가 무엇을 하는지 알고 있다와 도메인이 무엇을 하는지 알고 있다의 차이점이 있다.
어떤 방식으로 개발해야 맞고 TDD다 가 아닌 어떤 것을 알고 있는 지에 따라 개발 방식이 달라지는 것 같다..
인수 테스트는 given/when/then 형식의 시나리오를 작성하여 테스트를 진행한다. 나는 인수테스트를 위해 mock을 주로 사용했다. mock은 5주차미션을 진행하면서 거의 처음 사용하다시피 했다. mock은 메서드 안에 수행되는 repository 메서드같은 메서드들의 반환값을 예상?으로 결정하고 테스트를 하는 것이다. 기능을 알면 편리..하기도 하고 불편하기도 하다. mock 은 추후에 다시 정리를 해서 제대로 사용해야겠다.

1단계 - 인수 테스트 기반 리팩터링

1단계는 인수 테스트 코드를 작성하고 서비스로직 또는 도메인을 리펙토링 하는 것이다.
먼저 어떤 코드들을 domain으로 옮겨서 구현할지 결정하고 하나하나 천천히 테스트 코드를 작성하며 리팩토링을 진행했다.
  1. Domain으로 옮길 로직을 찾기
  • 스프링 빈을 사용하는 객체와 의존하는 로직을 제외하고는 도메인으로 옮길 예정
  • 객체지향 생활체조를 참고
  1. Domain의 단위 테스트를 작성하기
  • 서비스 레이어에서 옮겨 올 로직의 기능을 테스트
  • SectionsTest나 LineTest 클래스가 생성될 수 있음
  1. 로직을 옮기기
  • 기존 로직을 지우지 말고 새로운 로직을 만들어 수행
  • 정상 동작 확인 후 기존 로직 제거
5주차 미션의 리뷰어님은 wheejuni님!!
  • 첫 번째 리뷰는 컨벤션에 관한 내용이었다. it이 코틀린 컨벤션이었던 걸 몰랐다.
  • 인텔리제이의 자동 상수 단축키.. public 인지 private인지 제대로 확인하기!!

2단계 - 경로 조회 기능

경로 조회는 jgrapht 라이브러리를 이용해서 최단 거리를 구하는 단계이다.
@Test
public void getDijkstraShortestPath() {
    WeightedMultigraph<String, DefaultWeightedEdge> graph
            = new WeightedMultigraph(DefaultWeightedEdge.class);
    graph.addVertex("v1");
    graph.addVertex("v2");
    graph.addVertex("v3");
    graph.setEdgeWeight(graph.addEdge("v1", "v2"), 2);
    graph.setEdgeWeight(graph.addEdge("v2", "v3"), 2);
    graph.setEdgeWeight(graph.addEdge("v1", "v3"), 100);

    DijkstraShortestPath dijkstraShortestPath
            = new DijkstraShortestPath(graph);
    List<String> shortestPath 
            = dijkstraShortestPath.getPath("v3", "v1").getVertexList();

    assertThat(shortestPath.size()).isEqualTo(3);
}
각 구간별 거리를 set한후에 최단 거리를 구할 수 있다.
외부 라이브러리의 경우는 단위 테스트를 하지 않는다. 다만 외부 라이브러리를 사용해 직접 구현하는 로직을 검증할 수 는 있따. 이 때 실제 객체를 사용한다!
  • getter 메서드를 두고 정말 오래 고민했다. 물론 getter를 사용하지 않을 방법은 여러가지가 있다. 그 중에서 class에 메세지를 보내서 getter를 제거할 수 가 있는데 이렇게 오래 고민을 했던 건 일급컬렉션을 사용하면서 부터 시작되었다. 원시값을 포장 후 포장돤 객체들을 가지고 있는 일급컬렉션을 만들 다 보니 메세지가 타고 타고 타고 들어가서 불필요하다 싶을 정도로 수많은 메서드가 생성이 되기도 하고 여러가지 문제가 발생했다. 또 외부라이브러리와 도메인 객체간의 불필요한 의존이 생긴다고 생각했다.

  • 그래서 최대한 고민 후에 생각해낸 방법 중 가장 최선이라 생각한 방법으로 줄일 수 있도록 HashMap을 사용했었다.

  • 이 후 DM으로 조언을 구했다. 그에 대한 답변

  • 너무 한쪽으로 극단적으로 생각하지말고 여러가지 방면으로 생각하고 최선의 길을 선택할 수 있도록 연습해야겠다.

  • Factory Method의 사용.

3단계 - 인증을 통한 기능 구현

인증은 token을 사용하여 인증하는 기능을 이용했다
  • Bearer
    • 로그인을 통해 access token을 발급
    • 발급 받은 토큰을 oauth2()를 통하여 요청

3단계 정리하면서 할 일 : 인증 도구 정리하기, cascadetype 종류 공부, @AuthenticationPrincipal 공부하기

4단계 - 요금 조회

5주차 미션의 마지막 단계!는 요금조회 기능 구현이다. 요금조회는 각 기본 요금에 각 노선 별 추가 금, 연령별, 회원여부 에 따라 금액을 적용한다. 꽤나 까다로웠었는데. 먼저 요금과 정책을 가지고 있는 enum을 생성했다. 그리고 기본요금을 낼 class를 하나 만들고 연령별 등으로 class를 만들어 상속받아서 각각의 회원이 로그인을 하면 조건에 맞는 인스턴스를 생성하도록 구현했다.
  • stream을 이용해서 가장 큰 값을 가져오는 데 메서드를 몰라서 길게 구현을 했었다. 피드백을 받고 메서드를 다시 찾아보니 적당한 메서드를 찾았다.

마치며

5주차 미션에도 공부할 내용들이 정말 많았다. 그렇게 리뷰가 많았던 것은 아니라어서 금방 했다 보일지 모르지만 꽤 많이 고민하고 꽤 많은 시간을 들여 구현했다. jgrapht 라이브러리로 최단 거리를 조회하는 기능도 구현해보고 인증 도구를 사용해서 로그인한 사용자에 따라 자신의 정보를 조회하거나 회원 전용 기능을 구현하기도 했다.
전부 중요한 내용들이니 하나하나 다시 복습하는 생각으로 정리를 해나가야겠다.