Acceptance Test Driven Development(인수 테스트 주도 개발)

이번 미션에서는 ATDD(인수 테스트 주도 개발)에 대해 집중적으로 학습하기 위한 미션으로,
지하철 노선도 관리자 시스템의 각 기능들을 ATDD로 구현해보는 것이 목적이었다.

각 단계별로 지하철역 인수 테스트 작성, 지하철 노선 기능, 구간 추가 기능, 구간 제거 기능으로 구성되어 있었다.
지하철역 인수 테스트 작성 에서 ATDD를 맛보기로 경험하고,
비교적 익숙한 도메인인 지하철 노선도를 관리하는 기능을 직접 개발해봄으로써 ATDD를 익히기엔 충분한 미션이었다.

특히 RestAssured 를 통해 백엔드 중심의 인수테스트를 작성하였고,
기본적으로 frontend 구동에 필요한 소스코드와 환경은 제공하고 있었기 때문에,
화면을 통해 기능들을 확인해 볼 수 있다는 점이 인수테스트 주도 개발이라는 개념을 익히기에 좋은 점이었다고 생각한다.

이전 미션에 학습했던 JPA를 적극적으로 활용해야 한다는 점이 다소 어려웠지만,
단계적인 학습을 지향하는 나에게는 아주 좋은 경험이었다

회고

이번 미션에서 잘 한 점

인수테스트 작성시 given 을 위한 재활용 구성

인수테스트는 기능을 개발하기에 앞서 그 기능을 충족시키는 요구사항을 정의하고, 요구사항대로 테스트 코드를 짜는데 그 목적이 있다.
따라서 요구사항은 아주 크게 보면 given / when / then 이란 것을 정의하게 되는데 기능들을 개발하다 보면 이전에 개발했던 기능이
given 조건으로 들어와야 하는 경우가 있다.

예를 들면, 구간을 추가하는 기능은 노선과 역이 이미 존재하는 것을 필수조건으로 삼고, 이는 given 절에 들어갈 수 있다.
따라서 given 조건의 상태를 만들기 위해 역을 추가하거나 노선을 추가하는 등의 기능을 인수테스트용 util로 정의하고 적극적으로 활용하였다.

하나의 예를 들면, 노선을 생성하는 인수테스트를 아래와 같은 메서드로 테스트용 공통모듈에 추가하였다.
이 모듈을 구간 추가하는 인수테스트에선 given에서 활용하므로 테스트 코드가 가독성도 올라가고, 깔끔하게 작성 가능했다

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public static ExtractableResponse<Response> createLine(String name, String color, String upStationId, String downStationId, String distance) {
Map<String, String> params = getParams(name, color, upStationId, downStationId, distance);
return post(params, REQUEST_PATH_FOR_LINE, MediaType.APPLICATION_JSON_VALUE);
}

/**
* Given : 새로운 구간을 생성하고
* When : 새로운 구간의 길이가 기존 길이보다 작으면
* Then : 구간이 등록된다.
*/
@DisplayName("역 사이에 새로운 역을 등록할 경우, 새로운 길이를 뺀 나머지를 새롭게 추가된 역과의 길이로 설정")
@Test
void 역사이에_새로운역_등록_성공(){
// given
경강선=LineAcceptanceTestUtil.createLine("경강선","bg-blue-600",판교역,경기광주역,"10")
.jsonPath().getString("id");

...
}

어려웠던 점

예외 상황 정의에 대한 기술적인 부재

마지막 구간 제거 기능 단계에선 아래와 같은 요구사항이 존재했다.

  • 예외 케이스에 대한 검증도 포함하세요
  • 기능 설명을 참고하여 예외가 발생할 수 있는 경우를 검증할 수 있는 인수 테스트를 만들고 이를 성공 시키세요

한 마디로, 발생 가능할 것 같은 모든 예외케이스를 인수 테스트로 작성하고 예외를 발생시켜 테스트를 통과하는
안전한 코드를 작성하라는 것으로 이해했다.

예외에 대한 적절한 인수테스트를 잘 작성하고, 이에 대한 예외도 적절하게 발생하는 안전한 코드를 작성했지만,
나는 더 나아가서 동시성 문제에 대한 예외도 생각해보게 되었다.

처음으로 생각한 동시성 문제에 대한 예외 케이스는 아래와 같았다.

  • 이렇게 동시에 요청이 오는 경우가 드물긴 하겠지만, 만약 이런 경우가 생긴다면 어쩌지?
    나는 이런 문제를 예방하고자 List<Section> 의 구현체를 ArrayList에서 CopyOnWriteArrayList로 변경하였다.

결과부터 말하자면, 시간의 흐름에 따라 동시에 요청이 오는 경우가 있겠지만 내가 생각했던 동시성 문제는 발생하지 않는다.
왜냐하면, 동시성 문제라는 것 자체가 소스코드에서 스레드간 공유하는 자원을 동시에 제어할 때 발생하는 문제이기 때문이다.

리뷰어님께서는 아래와 같은 리뷰를 해주셨다.

(정말 친절하시게도 내가 한번 더 고민해볼 수 있도록 리뷰해주셨다..)

이를 토대로 학습해본 결과

  1. 하나의 request 당 thread pool 에서 한개의 thread를 할당 받는다
  2. thread간 공유하는 클래스에서 동시성 문제가 발생할 수도 있다

라는 결론을 도출할 수 있었고, 내 소스코드 상에선 thread 간 공유하는 자원이 없었기 때문에,
동시성 문제를 고려하지 않아도 됐었다.

추가로 대규모 대용량 서비스를 구축하거나 운영하는 관점에서는

  • 동시성 문제를 어떻게 인지하고 예방 혹은 대처하는 지
  • 동시성에 대한 테스트 방법
    등이 궁금해져서 관련 질문을 했고, 아래와 같은 답변을 받았다.

동시성 문제에 대해 학습해볼 키워드를 얻은 것 같아서, 아주 영양가 높은 리뷰였다🧐

동시성 문제 학습 키워드
  • 소스코드에서 발생할 수 있는 동시성 문제 해결법
  • DB에서 발생할 수 있는 동시성 문제와 해결법
  • java multi thread 를 이용한 테스트 코드 작성
  • stress test

느낀점

인수 테스트 주도 개발이란 용어 자체도 처음 접해본 나로써는 이번 미션이 다소 생소했다.
하지만, 요구사항을 토대로 테스트를 정의하고, 이를 통과하는 코드를 만드는 개발이란 개념이
SI 개발 에서도 통용될 수 있는 것이 아닐까? 라는 의문을 갖게 만들었다.
물론 인수 테스트는 잘 정의되어 있는 요구사항을 전제로 한다…

강의 시간에 관련된 책으로 린 애자일 기법을 활용한 (인수) 테스트 주도 개발을 소개해줬는데 이 책도 꼭 한번 읽어봐야겠다.
(영어 원문으로는 Lean-Agile Acceptance Test-Driven Development로 되어있다. Acceptance(인수) 라는 단어의 번역을 누락한 것 같다.)

ATDD를 제대로 학습하기