💋 인수테스트란?
- 소프트웨어 개발 과정에서 사용자의 요구사항과 기대에 부합하는지 검증하는 테스트
- 주로 사용자가 사용할 환경과 유사한 환경에서 수행
인수테스트의 '인수'는 '수용'을 뜻하는데, 소프트웨어 개발이 끝나고 사용자가 수용할 수 있는 상태가 되었을 때 테스트가 이루어지기 (통과되기) 때문에 이런 이름이다.
💋 인수테스트가 필요한 이유
인수테스트는 모든 것을 해결할 수는 없지만, 필요한 상황에 맞춰서 사용하는 도구이다.
우리는 개발하다가 길을 잃을 때가 종종 있다. 예를 들면, 다음에 어떤 일을 하려고 이 코드를 짜려고 했는 지 잊는 경우이다.
특히나 세부적인 기능을 구현하다 보면, 내가 궁극적으로 어떤 일을 하려고 했는 지 까먹을 때가 많다.
작은 단위의 세부적인 계획을 세우지 않고, 큰 단위로 계획을 세우다 보면 요구사항을 잘못 이해할 때도 있다.
요구사항에 대한 인수 테스트를 이용하여 요구사항을 명확히 하기 위해서,
(기획자부터) 개발자까지 모든 팀원이 요구사항에 대한 공통의 이해를 바탕으로 개발을 진행하는 방법이다.
내가 무엇을 개발하려 하는지를 명확하게 이해하는 것을 돕기 위해서 인수테스트가 필요하다!
💋 인수테스트의 장점
✔ 생산성 증가
구현 전에 인수 테스트를 수행하는 경우, 팀의 생산성이 두 배가 되는 것을 확인했다는 사람이 있었다. (누군지 기억은 안남)
왜일까? 불필요한 프로세스 반복을 줄일 수 있어서..?
개발 프로세스 중에요구사항을 잘못 이해했을 때, 다시 개발하는 부분이 프로젝트의 상당 부분을 차지하고 있다.
이 과정은 기획과 개발이 공통으로 함께 일어날 수 있는 부분이다. 기획을 하고 후에 개발을 통해 구현해서 가져온 후에서야 아 잘못되었는데, 해서 다시 하고… 또 다시하는 불필요한 프로세스를 제거할 수 있다.
또 인수테스트는 작업의 시작과 끝을 명확하게 제시한다.
개발 시작 전에 시나리오 형태의 인수 테스트를 작성하고 개발을 시작한다면, 인수 테스트가 성공하는 순간 작업이 끝나게 된다.
내가 무엇을 빠뜨렸는 지, 다음에 무엇을 해야 하는지에 대해서 생각할 필요가 없어진다.
개발이 끝나고 집에 가서 쉬고 있는데, 구현하지 않은 부분이나 버그에 대해서 해결하는 상황을 막아준다.
✔ 빠른 피드백
- 직접 실행해 본다던가, 배포 환경에서 실행해 봐야 하는데, 인수 테스트를 작성해 놓으면 곧바로 실행해 볼 수 있다.
- 직접 수동 테스트 없이 시나리오대로 잘 동작하는 지 확인할 수 있는 도구이다.
✔ 귀찮은 작업을 프로세스로 강제
- 테스트 작성, 문서화라는 귀찮은 작업을 프로세스로 강제할 수 있다.
- 따로 만들어야 하는 그런 작업을 할 필요가 없게 된다.
💋 인수테스트는 사용자의 스토리를 검증하기 위한 것이다.
인수테스트는 사용자의 스토리를 검증하는 기능 테스트로, 먼저 사용자의 시나리오를 작성한다.
*시나리오를 테스트할 때, 비정상적인 시나리오부터 테스트하는 것이 더 step이 적기 때문에 좋다.
- 클라이언트는 대체로 개발자가 아니기에, 내부 동작이 어떻게 구현되는 지 보다는 내가 요구한 시나리오가 잘 동작하는 지 확인하고 싶어 한다.
- 내부 구현이나 사용된 기술보다는, 표면적으로 확인할 수 있는 요소를 바탕으로 검증하려고 한다.
- 실제 사용하는 상황의 시나리오를 바탕으로 요구사항을 작성하고, 내부 기술이나 세부 구현에 의존하지 않는 인수 테스트를 만드는 것이 중요하다.
💋 ATDD (Acceptance Test Driven Develpment)
TDD와 크게 다르지 않다. 디테일한 부분에서 차이가 있을 뿐!
큰 사이클로 먼저 인수 테스트를 실패하도록 작성하고, 가장 큰 애플리케이션 단위의 시나리오 테스트인 인수 테스트가 성공할 때까지 일반적인 TDD 방식으로 더 작은 단위의 테스트를 작성하면서 인수 테스트가 성공할 때까지 구현을 하면 되는 것이다!
생산성이 향상된다고 하니, 한 번 시도해보쟈!
💋 인수테스트의 활용 예시
인수 테스트를 위해서는 정말 여러 가지 방법이 있다.
도구가 중요한 것이 아니라, 사용자의 스토리를 검증한다면 인수 테스트라고 볼 수 있다.
먼저, 아래와 같이 시나리오를 작성한다.
이 케이스의 예외 상황은 뭘까?
경로를 조회하고 싶은데 존재하지 않는 역에 대해 조회하는 경우
연결되지 않은 두 역에 대해서 최단 경로를 조회하는 경우
이정도가 있을 것 같다.
비정상적인 스토리부터 검증하는 인수 테스트를 작성한다.
@Test
void 존재하지_않는_역에_대해_최단_경로_조회를_요청하면_에외가_발생한다() {
final ExtractableResponse<Response> response = 두_역_사이의_최단_경로_조회_요청(1L, 2L);
assertThat(response.statusCode()).isEqualTo(HttpStatus.BAD_REQUEST.value());
}
@Test
void 연결되지_않은_두_역에_대해_최단_경로_조회를_요청하면_예외가_발생한다() {
final long 노선_9호선_아이디 = 노선_생성하고_아이디_반환(노선_9호선);
final long 고속터미널_아이디 = 역_생성하고_아이디_반환(역_고속터미널);
final long 사평역_아이디 = 역_생성하고_아이디_반환(역_사평역);
final long 노선_3호선_아이디 = 노선_생성하고_아이디_반환(노선_3호선);
final long 교대역_아이디 = 역_생성하고_아이디_반환(역_교대역);
final long 양재역_아이디 = 역_생성하고_아이디_반환(역_양재역);
노선에_최초의_역_2개_추가_요청(
new InitialSectionCreateRequest(
노선_9호선_아이디, 고속터미널_아이디, 사평역_아이디, 5
)
);
노선에_최초의_역_2개_추가_요청(
new InitialSectionCreateRequest(
노선_3호선_아이디, 교대역_아이디, 양재역_아이디, 5
)
);
final ExtractableResponse<Response> response = 두_역_사이의_최단_경로_조회_요청(고속터미널_아이디, 양재역_아이디);
assertThat(response.statusCode()).isEqualTo(HttpStatus.BAD_REQUEST.value());
}
여기서, 메서드와 변수 이름을 한글로 작성한 이유는 여기가 한국이니깐, 더 가독성 좋은 언어를 택한 것이다.
또한 위에서 사용한 다양한 요청과 요청 후 아이디 반환에 대한 내용은 test/steps 패키지에 저장해 놓았다.
노선 생성 및 생성 후 아이디 반환에 대한 값들은 아래처럼 public static하도록 모두 정의해 놓았다.
public static ExtractableResponse<Response> 노선_생성_요청(final LineCreateRequest request) {
return RestAssured
.given()
.contentType(APPLICATION_JSON_VALUE)
.body(request)
.when()
.post("/lines")
.then()
.extract();
}
public static long 노선_생성하고_아이디_반환(final LineCreateRequest request) {
return 노선_생성_요청(request).as(LineResponse.class).getId();
}
위의 내용들을 인수 테스트에서 공통적으로 사용하게 되는 것이다.
입력으로 들어갈 request 객체, 또는 requestParam은 미리 파라미터로 빼놓는 것이 좋다.
이제 이렇게 인수 테스트를 작성했다면, 모든 인수 테스트가 성공할 때까지 구현을 하고,
인수 테스트가 모두 통과한다면 "아 나 할 일 다 했구나!" 하고 자러 가면 될 것이다!!!
'TEST' 카테고리의 다른 글
[TEST] 테스트 데이터 격리: 테스트용 데이터베이스 분리, 트랜잭션 강제 롤백, @Transactional 어노테이션 사용 (0) | 2023.06.22 |
---|---|
[TEST] RestAssured: API를 테스트해보자! (0) | 2023.06.05 |