우아한테크코스 레벨2, ATDD에 대해서 학습한 내용을 정리한다.
인수테스트 및 ATDD에 대해서는 우아한ATDD 를 통해서 보다 자세히 알아 볼 수 있을 것 같다.
인수테스트란 무엇일까? 인수테스트는 시나리오(사용자 스토리)
기반으로 기능을 테스트하는 것을 말한다.
이런 인수테스트의 도움을 받으면 다음과 같은 장점을 누릴 수 있다.
그렇다면 인수 테스트가 없으면 어떨까? (without 인수 테스트)
반면 인수테스트를 통해서는? (with 인수 테스트)
빠른 피드백을 통해서
Q. ATDD는 언제 필요할까?
우리는 요구사항을 잘못 이해한 경험이나 이제 뭘 어떻게 개발해야할지 길을 잃는 경험을 한 번쯤을 해보게 된다. 그게 규모가 작은 프로젝트라고 하더라도 말이다.
그러면 이 때 올바른 요구사항을 하도록 유도하는 것이 있다면 어떨까?
-> ATDD를 통해 효율적인 개발 프로세스를 경험함으로써 위와 같은 문제를 극복해볼 수 있다.
테스트가 가능한 요구사항
으로 소프트웨어를 개발하는 프로세스 (개발 방법론 중 하나)이다.
TDD는 write a failing unit test -> make the test pass -> refactor 의 흐름으로 진행된다.
TDD의 테스트는 작은 단위의 요구사항을 통해서 내가 앞으로 개발해 나가야할 것을 가리킨다.
ATDD는 어떤 특정 테스트를 가지고 구현을 하는 것인데, TDD는 작은 단위의 요구사항이라고 할 수 있지만 ATDD는 시나리오 형태의 요구사항이다. 즉, 시나리오 형태의 요구사항을 통해서 내가 앞으로 개발해 나가야할 것을 가리킨다.
"요구사항을 검증하는 테스트"로 소프트웨어를 개발하는 프로세스를 ATDD라고 할 수 있다.
1. 생산성 증가
코드 작성 전에 테스트 작성을 통해서 생산성을 효과적으로 향상할 수 있다. 왜냐하면 요구사항이 명확해지기 때문이다.
또한 테스트로 명확하게 요구사항을 명시함으로써 이게 충족되는지 안되는지를 테스트를 돌림으로써 바로 확인할 수 있다.
2. 작업의 명확한 시작과 끝을 제시
3. 빠른 피드백
직접 구동을 시켜볼 필요가 없다. 즉, 서버를 띄우고 웹 페이지에서 클릭을 하면서 테스트를 해볼 필요가 없어진다. 따라서 빠른 피드백을 받아볼 수 있다.
4. 귀찮은 작업을 프로세스로 강제
출처: Agile Acceptance Test Driven Development
인수테스트(Acceptance Test)란 앞서 언급한 것과 같이 사용자 스토리를 검증하는 기능 테스트로, 사용자 스토리로 테스트할 시나리오를 결정한다.
테스트에 종류는 다양하다.
단위 테스트
통합 테스트
E2E 테스트
인수 테스트
이렇게 테스트의 종류가 다양한데, 각 테스트는 무엇
을 검증하려고 하는지 그 목적에 따라 나뉜다고 볼 수 있다.
인수테스트는 명세나 계약의 요구사항이 충족되는지 확인
하기 위해 수행되는 테스트이다. 즉 이것이 인수테스트의 목적이다. 또한 앞서 잠깐 언급된 대로 작업의 시작과 끝을 명확히 제시한다. 즉, 작업을 종료 시켜도 되는지 검증하는 테스트로 이 테스트가 성공하면 작업이 끝이다를 명확히 할 수 있다.
우리는 이런 시나리오 기반의 인수테스트를 API 개발을 테스트하기 위해서 사용할 수 있다. API 레벨의 테스트는 다음과 같다.
@DisplayName("지하철역을 생성한다.")
@Test
void createStation() {
// given
Map<String, String> params = new HashMap<>();
params.put("name", "강남역");
// when
ExtractableResponse<Response> response = RestAssured.given().log().all()
.body(params)
.contentType(MediaType.APPLICATION_JSON_VALUE)
.when()
.post("/stations")
.then().log().all()
.extract();
// then
assertThat(response.statusCode()).isEqualTo(HttpStatus.CREATED.value());
assertThat(response.header("Location")).isNotBlank();
}
이런 인수테스트는 테스트의 의도에 따라서 구현 방법이 달라질 수 있다.
인수테스트는 블랙 박스 테스트의 성격을 가지고 있다. 즉, 내부 구현에는 관심이 없다. 단지 우리는 인수테스트를 함으로써 요청과 응답 기반의 검증을 원한다.
블랙박스?
SpringBootTest
- 테스트에 사용할 ApplicationContext를 쉽게 지정하게 도와준다.
@ContextConfiguration
의 발전된 기능이다.webEnvironment
@SpringBootTest
의 webEnvironment
속성을 이용하여 테스트 서버의 실행 방법을 설정할 수 있다.
RestAssured
- REST-assured는 REST API의 테스트 및 검증을 단순화하도록 설계된 것으로 HTTP 작업에 대한 검증을 위한 풍부한 API를 제공한다.
MockMvc
@SpringBootTest
의 webEnvironment.MOCK
과 함께 사용 가능하며 모킹된 web environemnt (ex. tomcat) 에서 테스트한다.
WebTestClient
@SpringBOotTest
의 webEnvironment.RANDOM_PORT
나 DEFINED_PORT
오 함께 사용하며 Netty를 기본으로 사용한다.
JsonPath
$
키워드 : 특정 필드만 꺼내오기 가능
인수 텐스트 격리
- Context Caching : SpringBootTest를 사용하여 테스트를 수행할 경우 context를 로드하는 시간이 많이 드는데 이를 위해 context를 캐싱하여 재사용하도록 동작한다.
협력 객체가 있는 단위 테스트를 만들 때 어떻게 해야 할까?
Sociable & Solitary (통합과 고립)
협력 객체를 실제 객체로 사용하는지 Mock 객체로 사용하는지에 따라 테스트 구현이 달라진다.
Test Double
실제 객체 대신 사용되는 모든 종류의 객체에 대한 일반적인 용어이다.
classicist vs mockist
- 협력 객체가 필요한 테스트는 단위테스트일까? 통합테스트일까?