REST API 테스트 REST Assured로 쉽게하기
익스랩 최고 관리자
·2020. 2. 21. 17:32
REST Assured Java 라이브러리를 사용하여 REST 어플리케이션의 HTTP Endpoint에 초첨을 맞춘,
API 테스트 코드 작성 방법에 대해 학습해봅시다.
Introduction
Java 클래스의 동작을 확인하는 Unit 테스트의 수행은, 테스트 전략의 첫 단계일 뿐입니다.
개별 Java클래스가 독립작으로 잘 작동한다고 해서
모든 클래스가 함께 묶일 때 어플리케이션 자체가 올바르게 작동한다는 것을 의미하는 것은 아닙니다.
기본 단위테스트 외에도 통합테스트(모듈에 초점을 맞춘 테스트),
기능 테스트 (배포된대로 어플리케이션을 사용하는 end-to-end 테스트),
사용자 승인 테스트 (GUI 테스트) 가 있습니다.
이 포스팅에서는 자바 클래스로 직접 작동하지 않는 기능 테스트를 다룰 것입니다.
오늘날의 대부분 어플리케이션은 JSON 데이터를 주고 받는 일종의 HTTP endpoint로 API를 노출합니다.
이러한 Endpoint는 GUI 레이어 또는 다른 기술의 back-end application에서 사용할 수 있습니다.
전체 개발 라이프 사이클을 적절하게 다루고, 테스팅 피라미드 패러다임을 올바르게 따르기를 원한다면
이러한 HTTP Endpoint가 적절하게 잘 작동하는지 확인해야 합니다.
학습 목표
1. REST Assured - API 테스트용 Java 라이브러리 다운로드 및 설정
2. HTTP Endpoint와의 상호 작용을 수행하는 간단한 테스트
3. 클라이언트와 HTTP Endpoint 사이에 "conversation"이 필요한 보다 복잡한 기능 테스트
4. Request, Response의 다양한 방법
Rest Assured를 사용한 REST API 테스트는 Black box 테스트입니다.
어플리케이션에 Request를 보내고, Response를 받고 미리 결정된 결과와 비교합니다.
테스트 대상 어플리케이션이 어떤 언어로 작성이 되었는지는, REST Assured와는 관련이 없습니다.
REST Assured는 테스트를 JSON 및 HTTP를 기반으로 합니다.
REST Assured를 사용하여 Java 뿐만 아니라
Python, Ruby, .Net 등으로 작성된 어플리케이션에 대한 테스트 코드를 작성할 수 있습니다.
어플리케이션이 HTTP endpoint를 제공하는 한, REST Assured는 구현 프로그래밍 언어와 상관 없이 테스트할 수 있습니다만,
구현과 단위테스트에 동일한 언어를 사용할 것이기 때문에
JAVA 어플리케이션용 REST Assured를 사용하는 것이 편리할 것입니다.
사전 조건
- HTTP/REST/JSON API 가 있는 샘플 JAVA 프로젝트
- 프로젝트를 빌드하는 유효한 pom.xml 파일
- Maven 설치
- Maven dependency를 다운로드하기 위한 인터넷 연결
REST Assured는 JUnit에서 작동하므로, JUnit에 대한 어느정도의 지식은 갖추고 있어야 합니다. Hamcrest 지식은 도움이 되지만 우리는 Hamcrest matchers를 사용할 것이기 때문에 필수적으로 알아야 할 지식은 아닙니다.
또한, Maven 빌드에 대한 방법을 이미 알고 있다고 가정하겠습니다.
REST Assured 소개
REST Assured는 REST 웹 서비스를 검증하기 위한 Java 라이브러리입니다.
HTTP endpoint에 대한 연결 및 예상되는 결과를 설명하는 친숙한 DSL (Domain Specific Languages)을 제공합니다.
간단한 예제를 살펴볼까요?
1 2 3 4 5 6 7 8 9 10 11 | import org.junit.Test; import io.restassured.RestAssured; public class SampleTest { @Test public void test() { RestAssured.given() .when().get("http://www.google.com") .then().statusCode(200); } } | cs |
이 JUnit 테스트는 Google에 연결하고, GET 호출을 수행하며, HTTP 코드 200/success가 반환되는지 확인합니다.
일반적인 JUnit assert 문이 없습니다.
REST Assured는 반환된 status code에 따라 자동으로 pass 또는 fail을 판단합니다.
REST Assured 설정
pom.xml 에 다음과 같은 Dependency를 추가해주어야 합니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | <dependencies> <dependency> <groupId>io.rest-assured</groupId> <artifactId>rest-assured</artifactId> <version>3.0.3</version> <scope>test</scope> </dependency> <dependency> <groupId>com.google.code.gson</groupId> <artifactId>gson</artifactId> <version>2.8.1</version> <scope>test</scope> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> </dependencies> | cs |
GSON은 API를 Request Body에 Json형식으로 호출할 때, 자동으로 Json 형식으로 호출하도록 해줍니다.
REST Assured 특징
REST Assured 사용법
Parameters
1 2 3 4 5 | given(). param("param1", "value1"). param("param2", "value2"). when(). get("/something"); | cs |
1 2 3 4 5 | given(). formParam("formParamName", "value1"). queryParam("queryParamName", "value2"). when(). post("/something"); | cs |
여러 값을 가지고 있는 parameter의 경우에는
1 | given().param("myList", "value1", "value2" | cs |
다음처럼 parameter명과 값들을 적어주거나 List에 담아 호출합니다.
1 2 3 4 5 | List<String> values = new ArrayList<String>(); values.add("value1"); values.add("value2"); given().param("myList", values) | cs |
path parameter
1 | post("/reserve/{hotelId}/{roomNumber}", "My Hotel", 23); | cs |
Response JSON의 Assersion 정의
Response Data 검증
Sample Java Application
실습 사이트 : https://jsonplaceholder.typicode.com/
이 사이트는 Rest API 테스트를 해볼 수 있는, Online Fake API를 제공하고 있습니다.
참고 : HTTP 상태 코드
이 사이트에서는 다음과 같은 Resources와 Routes가 있습니다.
Resources
Inspired by common use cases.
/posts | 100 items |
/comments | 500 items |
/albums | 100 items |
/photos | 5000 items |
/todos | 200 items |
/users | 10 items |
Routes
All HTTP verbs are supported.
View usage examples.
GET | /posts |
GET | /posts/1 |
GET | /posts/1/comments |
GET | /comments?postId=1 |
GET | /posts?userId=1 |
POST | /posts |
PUT | /posts/1 |
PATCH | /posts/1 |
DELETE | /posts/1 |
그 중에 몇가지 함께 해봅시다.
모든 Post를 조회하는 API
1 2 3 4 5 6 7 | @Test public void getPostsTest() { RestAssured.given() .when().get("/posts") .then().statusCode(200) .log().all(); } | cs |
포스트를 등록하는 API
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | @Test public void addPostTest() { Map<String, Object> requestData = new HashMap<>(); requestData.put("title", "foo"); requestData.put("body", "bar"); requestData.put("userId", 1); RestAssured.given() .contentType("application/json") .body(requestData).log().all() .when() .post("/posts") .then() .statusCode(201) .assertThat().body("title", equalTo("foo")) .assertThat().body("body", equalTo("bar")) .assertThat().body("userId", equalTo(1)) .log().all(); } | cs |
포스트를 수정하는 API
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | @Test public void updatePostTest() { Map<String, Object> requestData = new HashMap<>(); requestData.put("id", 1); requestData.put("title", "fooupdate"); requestData.put("body", "bar update"); requestData.put("userId", 1); RestAssured.given() .contentType("application/json") .pathParam("postId", 1) .body(requestData) .log().all() .when() .put("/posts/{postId}") .then() .statusCode(200) .assertThat().body("id", equalTo(1)) .assertThat().body("title", equalTo("fooupdate")) .assertThat().body("body", equalTo("bar update")) .assertThat().body("userId", equalTo(1)) .log().all(); } | cs |
포스트를 삭제하는 API
1 2 3 4 5 6 7 8 9 10 11 12 | @Test public void deletePostTest() { RestAssured.given() .pathParam("postId", 1) .log().all() .when() .delete("/posts/{postId}") .then() .statusCode(200) .log().all(); } | cs |