Sample API 테스트 코드
모든 Sample API 의 테스트 코드
BaseTest 클래스 상속
BaseTest 클래스를 상속받아서 테스트 코드를 구현했다
BaseTest 코드의 생성자만 구현한 뒤 사용하면 된다
/**
* 샘플 테스트 코드
* BaseTest 상속 받으면 미리 설정해둔 어노테이션들이 자동으로 셋팅됨
* Created by KMS on 11/03/2020.
*/
class SampleApiControllerTest extends BaseTest {
/**
* 상속 클래스의 생성자 생성
* @param mapper
* @param ctx
*/
public SampleApiTest(ObjectMapper mapper, WebApplicationContext ctx) {
super(mapper, ctx);
}
...
}
@BeforeAll
@BeforeAll
어노테이션을 붙인 메서드에 테스트 시작전 종합 테스트 성능 측정 totalStopWatch 생성하였다 최초 1회 수행
totalStopWatch는 이미 BaseTest에 protected static
으로 선언되어 있기 때문에 상속을 받은 클래스에서 사용할 수 있다
※@BeforeAll
어노테이션은 static
메서드로만 구현할 수 있다
/**
* 테스트 시작전 최초 1회 수행
*/
@BeforeAll
static void start() {
// 종합 테스트 성능 측정 상속 클래스에 protected 로 지정한 totalStopWatch 사용
totalStopWatch = new StopWatch("API Controller Total Tests");
}
@BeforeEach
@BeforeEach
어노테이션으로 지정한 메서드는 모든 테스트 전에 한번씩 수행된다
StopWatch를 매 테스트 마다 생성하여 개발 성능 측정을 매번 수행 한다
테스트에서 사용되는 테스트 객체도 매 테스트 마다 생성하여 테스트 코드에서 사용할 수 있게 한다
/**
* 모든 테스트 수행 시작시마다 반복적으로 수행됨
*/
@BeforeEach
void setData() {
// 개별 테스트 성능 측정
stopWatch = new StopWatch("API Controller Tests");
stopWatch.start();
Address address = Address.builder()
.country("ameria")
.state("si")
.city("seoul")
.zipCode("12345")
.build();
reqGetSample = ReqGetSample.builder()
.myId("id")
.myName("name")
.myAge(18)
.myEmail("abc@choco.com")
.myJob("Developer")
.hobby(Hobby.MOVIE)
.myAddress(address)
.build();
reqJsonSample = ReqJsonSample.builder()
.myId("id")
.myName("name")
.myAge(18)
.myEmail("abc@choco.com")
.myJob("Developer")
.myAddress(address)
.build();
}
TestUtils.objectToMultiValueMap(object)
추가로 GET 방식에서 유용하게 사용할 수 있는 Util 을 개발 하였는데
GET 타입으로 객체 타입으로 값을 전달하는 테스트코드를 구현해야 할 때Map
으로 파라메터 객체를 구현하여 다시 String
형태로 변환해야 된다던지MultiValueMap
으로 다중 파라메터로 구현하여 처리 해야된다던지 매우 번거롭다
이 Util을 사용하면 객체를 .params
에 사용할 수 있는 MultiValueMap
타입으로 변환해준다
GET 타입의 테스트 코드를 작성할 때 아주 유용하게 활용할 수 있다
TestUtils.objectToMultiValueMap(reqGetSample)
Sample API 테스트 코드
각 테스트가 순서대로 실행 될 수 있게 @Order
어노테이션으로 순번을 매겨 놓았으며
참고 삼아 jsonPath
활용하여 리턴값을 검증하는 코드를 추가 해놓았다
각 테스트 마다 @DisplayName
에 알아보기 쉬운 명칭을 지정해 주었다
매 테스트 마다 종합 성능측정을 위해 totalStopWatch.start("GET - CALL 테스트");
와 같이 totalStopWatch
를 start
시킨다
@Test
@Order(1)
@DisplayName("GET - CALL 테스트")
void call() throws Exception {
totalStopWatch.start("GET - CALL 테스트");
MvcResult result = mockMvc.perform(get("/call"))
.andExpect(status().isOk())
.andDo(print())
.andReturn();
String content = result.getResponse().getContentAsString();
assertThat(content).isEqualTo("ok");
}
@Test
@Order(2)
@DisplayName("GET - 서버 접속 테스트")
void test1() throws Exception {
totalStopWatch.start("GET - 서버 접속 테스트");
mockMvc.perform(get("/test")
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(jsonPath("code").value(100))
.andExpect(jsonPath("result").isArray())
.andExpect(jsonPath("result[0]").value(1))
.andExpect(jsonPath("result[1]").value(2))
.andExpect(jsonPath("result[2]").value(3))
.andExpect(jsonPath("result[0]").exists())
.andDo(print());
}
@Test
@Order(3)
@DisplayName("GET - 에러 테스트")
void error() throws Exception {
totalStopWatch.start("GET - 에러 테스트");
mockMvc.perform(get("/err"))
.andExpect(status().isOk())
.andExpect(header().exists(HttpHeaders.VARY)) // HEADER에 Location 있는지 확인
.andExpect(header().string(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_UTF8_VALUE)) //Content-Type 값 확인
.andExpect(jsonPath("code").value(500))
.andDo(print());
}
@Test
@Order(4)
@DisplayName("GET - Object 파라메터 요청 테스트")
void sampleGet() throws Exception {
totalStopWatch.start("GET - Object 파라메터 요청 테스트");
mockMvc.perform(get("/sample")
.params(TestUtils.objectToMultiValueMap(reqGetSample)))
.andExpect(status().isOk())
.andExpect(jsonPath("code").value(100))
.andExpect(jsonPath("result").isMap())
.andExpect(jsonPath("result").isNotEmpty())
.andExpect(jsonPath("result.seq").isEmpty())
.andExpect(jsonPath("result.myId").exists())
.andExpect(jsonPath("result.myId").isString())
.andExpect(jsonPath("result.myId").value("id"))
.andExpect(jsonPath("result.myName").exists())
.andExpect(jsonPath("result.myName", is("name")))
.andExpect(jsonPath("result.myName", hasLength(4)))
.andExpect(jsonPath("result.myName").value("name"))
.andExpect(jsonPath("result.myAge").exists())
.andExpect(jsonPath("result.myAge").isNumber())
.andExpect(jsonPath("result.myEmail").exists())
.andExpect(jsonPath("result.myEmail", hasLength(13)))
.andExpect(jsonPath("result.myEmail").value("abc@choco.com"))
.andExpect(jsonPath("result.myJob").exists())
.andExpect(jsonPath("result.myJob").value("Developer"))
.andExpect(jsonPath("result.hobby").exists())
.andExpect(jsonPath("result.hobby").value(Hobby.MOVIE.name()))
.andExpect(jsonPath("result.myAddress.country").exists())
.andExpect(jsonPath("result.myAddress.country").value("ameria"))
.andExpect(jsonPath("result.myAddress.state").exists())
.andExpect(jsonPath("result.myAddress.state").value("si"))
.andExpect(jsonPath("result.myAddress.city").exists())
.andExpect(jsonPath("result.myAddress.city").value("seoul"))
.andExpect(jsonPath("result.myAddress.zip_code").exists())
.andExpect(jsonPath("result.myAddress.zip_code").value(12345))
.andDo(print());
}
@Test
@Order(5)
@DisplayName("GET - Object PathVariable 파라메터 요청 테스트")
void sampleGetPathVariable() throws Exception {
totalStopWatch.start("GET - Object PathVariable 파라메터 요청 테스트");
mockMvc.perform(get("/sample/{seq}", 1)
.params(TestUtils.objectToMultiValueMap(reqGetSample)))
.andExpect(status().isOk())
.andExpect(jsonPath("code").value(100))
.andExpect(jsonPath("result").isMap())
.andExpect(jsonPath("result").isNotEmpty())
.andExpect(jsonPath("result.seq").exists())
.andExpect(jsonPath("result.seq").isNumber())
.andExpect(jsonPath("result.seq").value(1))
.andDo(print());
}
@Test
@Order(6)
@DisplayName("GET - Category Object PathVariable 파라메터 요청 테스트")
void sampleGetCategoryPathVariable() throws Exception {
totalStopWatch.start("GET - Category Object PathVariable 파라메터 요청 테스트");
mockMvc.perform(get("/sample/hobby/{hobby}", Hobby.GAME.name())
.params(TestUtils.objectToMultiValueMap(reqGetSample)))
.andExpect(status().isOk())
.andExpect(jsonPath("code").value(100))
.andExpect(jsonPath("result").isMap())
.andExpect(jsonPath("result").isNotEmpty())
.andExpect(jsonPath("result.hobby").exists())
.andExpect(jsonPath("result.hobby").isString())
.andExpect(jsonPath("result.hobby").value(Hobby.GAME.name()))
.andExpect(jsonPath("result.hobby", hasLength(4)))
.andDo(print());
}
@Test
@Order(7)
@DisplayName("POST - @RequestBody String 파라메터 테스트")
void bodyString() throws Exception {
totalStopWatch.start("POST - @RequestBody String 파라메터 테스트");
mockMvc.perform(post("/sample/body-string")
.contentType(MediaType.APPLICATION_JSON)
.content(mapper.writeValueAsString(reqJsonSample)))
.andExpect(status().isOk())
.andDo(print());
}
@Test
@Order(8)
@DisplayName("POST - @RequestBody Object 파라메터 테스트")
void bodyObject() throws Exception {
totalStopWatch.start("POST - @RequestBody Object 파라메터 테스트");
mockMvc.perform(post("/sample/body-object")
.contentType(MediaType.APPLICATION_JSON)
.content(mapper.writeValueAsString(reqJsonSample)))
.andExpect(status().isOk())
.andDo(print());
}
@Test
@Order(9)
@DisplayName("PATCH - @RequestBody Object PathVariable 파라메터 테스트")
void patchBodyObjectPathvariable() throws Exception {
totalStopWatch.start("PATCH - @RequestBody Object PathVariable 파라메터 테스트");
mockMvc.perform(patch("/sample/pathvariable-object/{seq}", 1)
.contentType(MediaType.APPLICATION_JSON)
.content(mapper.writeValueAsString(reqJsonSample)))
.andExpect(status().isOk())
.andDo(print());
}
@Test
@Order(10)
@DisplayName("DELETE - @RequestBody Object PathVariable 파라메터 테스트")
void deleteObjectPathVariable() throws Exception {
totalStopWatch.start("DELETE - @RequestBody Object PathVariable 파라메터 테스트");
mockMvc.perform(delete("/sample/pathvariable-object/{seq}", 1)
.contentType(MediaType.APPLICATION_JSON)
.params(TestUtils.objectToMultiValueMap(reqGetSample)))
.andExpect(status().isOk())
.andDo(print());
}
@AfterEach
@AfterEach
어노테이션을 붙인 메서드는 매 테스트 수행 종료시마다 반복적으로 수행되는 메서드 이다
매 테스트 종료시 마다 개별 테스트 성능 측정을 위한 StopWatch 를 종료 시키고 개별 테스트 성능 측정 결과를 출력한다
종합 성능 측정을 위한 totalStopWatch
도 매 테스트 마다 종료 시킨다
/**
* 모든 테스트 수행 종료시마다 반복적으로 수행됨
*/
@AfterEach
void stop() {
stopWatch.stop();
System.out.println("===================================================");
System.out.println("개별 테스트 성능 측정 결과");
System.out.println("===================================================");
System.out.println("Time Seconds = "+stopWatch.getTotalTimeSeconds()+"s");
System.out.println("Time Millis = "+stopWatch.getTotalTimeMillis()+"ms");
System.out.println("Time Nanos = "+stopWatch.getTotalTimeNanos()+"ns");
//System.out.println(stopWatch.shortSummary());
System.out.println(stopWatch.prettyPrint());
System.out.println("===================================================");
totalStopWatch.stop();
}
@AfterAll
@AfterAll
어노테이션을 붙인 메서드는 모든 테스트가 종료된 후 최초 1회 수행되는 메서드이다@AfterAll
어노테이션을 붙인 메서드에서 종합 테스트 성능 측정 totalStopWatch 의 종합 테스트 성능 측정 결과를 출력한다
※@AfterAll
어노테이션도 @BeforeAll
어노테이션과 마찬가지로 static
메서드로만 구현할 수 있다
SampleApiControllerTest 클래스 전체 코드
SampleApiControllerTest
클래스의 전체 코드이다
package com.iparking.api.sample.controller;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.iparking.api.sample.constant.Hobby;
import com.iparking.api.sample.model.Address;
import com.iparking.api.sample.packet.ReqGetSample;
import com.iparking.api.sample.packet.ReqJsonSample;
import com.iparking.common.BaseControllerTest;
import com.iparking.utils.common.TestUtils;
import org.junit.jupiter.api.*;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.util.StopWatch;
import org.springframework.web.context.WebApplicationContext;
import static org.assertj.core.api.Assertions.assertThat;
import static org.hamcrest.Matchers.hasLength;
import static org.hamcrest.Matchers.is;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
/**
* 샘플 테스트 코드
* BaseTest를 상속 받으면 미리 설정해둔 어노테이션들이 자동으로 셋팅됨
* Created by KMS on 11/03/2020.
*/
class SampleApiControllerTest extends BaseTest {
/**
* 상속 클래스의 생성자 생성
* @param mapper
* @param ctx
*/
public SampleApiTest(ObjectMapper mapper, WebApplicationContext ctx) {
super(mapper, ctx);
}
private ReqGetSample reqGetSample;
private ReqJsonSample reqJsonSample;
/**
* 테스트 시작전 최초 1회 수행
*/
@BeforeAll
static void start() {
// 종합 테스트 성능 측정 상속 클래스에 protected 로 지정한 totalStopWatch 사용
totalStopWatch = new StopWatch("API Controller Total Tests");
}
/**
* 모든 테스트 수행 시작시마다 반복적으로 수행됨
*/
@BeforeEach
void setData() {
// 개별 테스트 성능 측정
stopWatch = new StopWatch("API Controller Tests");
stopWatch.start();
Address address = Address.builder()
.country("ameria")
.state("si")
.city("seoul")
.zipCode("12345")
.build();
reqGetSample = ReqGetSample.builder()
.myId("id")
.myName("name")
.myAge(18)
.myEmail("abc@choco.com")
.myJob("Developer")
.hobby(Hobby.MOVIE)
.myAddress(address)
.build();
reqJsonSample = ReqJsonSample.builder()
.myId("id")
.myName("name")
.myAge(18)
.myEmail("abc@choco.com")
.myJob("Developer")
.myAddress(address)
.build();
}
@Test
@Order(1)
@DisplayName("GET - CALL 테스트")
void call() throws Exception {
totalStopWatch.start("GET - CALL 테스트");
MvcResult result = mockMvc.perform(get("/call"))
.andExpect(status().isOk())
.andDo(print())
.andReturn();
String content = result.getResponse().getContentAsString();
assertThat(content).isEqualTo("ok");
}
@Test
@Order(2)
@DisplayName("GET - 서버 접속 테스트")
void test1() throws Exception {
totalStopWatch.start("GET - 서버 접속 테스트");
mockMvc.perform(get("/test")
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(jsonPath("code").value(100))
.andExpect(jsonPath("result").isArray())
.andExpect(jsonPath("result[0]").value(1))
.andExpect(jsonPath("result[1]").value(2))
.andExpect(jsonPath("result[2]").value(3))
.andExpect(jsonPath("result[0]").exists())
.andDo(print());
}
@Test
@Order(3)
@DisplayName("GET - 에러 테스트")
void error() throws Exception {
totalStopWatch.start("GET - 에러 테스트");
mockMvc.perform(get("/err"))
.andExpect(status().isOk())
.andExpect(header().exists(HttpHeaders.VARY)) // HEADER에 Location 있는지 확인
.andExpect(header().string(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_UTF8_VALUE)) //Content-Type 값 확인
.andExpect(jsonPath("code").value(500))
.andDo(print());
}
@Test
@Order(4)
@DisplayName("GET - Object 파라메터 요청 테스트")
void sampleGet() throws Exception {
totalStopWatch.start("GET - Object 파라메터 요청 테스트");
mockMvc.perform(get("/sample")
.params(TestUtils.objectToMultiValueMap(reqGetSample)))
.andExpect(status().isOk())
.andExpect(jsonPath("code").value(100))
.andExpect(jsonPath("result").isMap())
.andExpect(jsonPath("result").isNotEmpty())
.andExpect(jsonPath("result.seq").isEmpty())
.andExpect(jsonPath("result.myId").exists())
.andExpect(jsonPath("result.myId").isString())
.andExpect(jsonPath("result.myId").value("id"))
.andExpect(jsonPath("result.myName").exists())
.andExpect(jsonPath("result.myName", is("name")))
.andExpect(jsonPath("result.myName", hasLength(4)))
.andExpect(jsonPath("result.myName").value("name"))
.andExpect(jsonPath("result.myAge").exists())
.andExpect(jsonPath("result.myAge").isNumber())
.andExpect(jsonPath("result.myEmail").exists())
.andExpect(jsonPath("result.myEmail", hasLength(13)))
.andExpect(jsonPath("result.myEmail").value("abc@choco.com"))
.andExpect(jsonPath("result.myJob").exists())
.andExpect(jsonPath("result.myJob").value("Developer"))
.andExpect(jsonPath("result.hobby").exists())
.andExpect(jsonPath("result.hobby").value(Hobby.MOVIE.name()))
.andExpect(jsonPath("result.myAddress.country").exists())
.andExpect(jsonPath("result.myAddress.country").value("ameria"))
.andExpect(jsonPath("result.myAddress.state").exists())
.andExpect(jsonPath("result.myAddress.state").value("si"))
.andExpect(jsonPath("result.myAddress.city").exists())
.andExpect(jsonPath("result.myAddress.city").value("seoul"))
.andExpect(jsonPath("result.myAddress.zip_code").exists())
.andExpect(jsonPath("result.myAddress.zip_code").value(12345))
.andDo(print());
}
@Test
@Order(5)
@DisplayName("GET - Object PathVariable 파라메터 요청 테스트")
void sampleGetPathVariable() throws Exception {
totalStopWatch.start("GET - Object PathVariable 파라메터 요청 테스트");
mockMvc.perform(get("/sample/{seq}", 1)
.params(TestUtils.objectToMultiValueMap(reqGetSample)))
.andExpect(status().isOk())
.andExpect(jsonPath("code").value(100))
.andExpect(jsonPath("result").isMap())
.andExpect(jsonPath("result").isNotEmpty())
.andExpect(jsonPath("result.seq").exists())
.andExpect(jsonPath("result.seq").isNumber())
.andExpect(jsonPath("result.seq").value(1))
.andDo(print());
}
@Test
@Order(6)
@DisplayName("GET - Category Object PathVariable 파라메터 요청 테스트")
void sampleGetCategoryPathVariable() throws Exception {
totalStopWatch.start("GET - Category Object PathVariable 파라메터 요청 테스트");
mockMvc.perform(get("/sample/hobby/{hobby}", Hobby.GAME.name())
.params(TestUtils.objectToMultiValueMap(reqGetSample)))
.andExpect(status().isOk())
.andExpect(jsonPath("code").value(100))
.andExpect(jsonPath("result").isMap())
.andExpect(jsonPath("result").isNotEmpty())
.andExpect(jsonPath("result.hobby").exists())
.andExpect(jsonPath("result.hobby").isString())
.andExpect(jsonPath("result.hobby").value(Hobby.GAME.name()))
.andExpect(jsonPath("result.hobby", hasLength(4)))
.andDo(print());
}
@Test
@Order(7)
@DisplayName("POST - @RequestBody String 파라메터 테스트")
void bodyString() throws Exception {
totalStopWatch.start("POST - @RequestBody String 파라메터 테스트");
mockMvc.perform(post("/body-string")
.contentType(MediaType.APPLICATION_JSON)
.content(mapper.writeValueAsString(reqJsonSample)))
.andExpect(status().isOk())
.andDo(print());
}
@Test
@Order(8)
@DisplayName("POST - @RequestBody Object 파라메터 테스트")
void bodyObject() throws Exception {
totalStopWatch.start("POST - @RequestBody Object 파라메터 테스트");
mockMvc.perform(post("/body-object")
.contentType(MediaType.APPLICATION_JSON)
.content(mapper.writeValueAsString(reqJsonSample)))
.andExpect(status().isOk())
.andDo(print());
}
@Test
@Order(9)
@DisplayName("PATCH - @RequestBody Object PathVariable 파라메터 테스트")
void patchBodyObjectPathvariable() throws Exception {
totalStopWatch.start("PATCH - @RequestBody Object PathVariable 파라메터 테스트");
mockMvc.perform(patch("/pathvariable-object/{seq}", 1)
.contentType(MediaType.APPLICATION_JSON)
.content(mapper.writeValueAsString(reqJsonSample)))
.andExpect(status().isOk())
.andDo(print());
}
@Test
@Order(10)
@DisplayName("DELETE - @RequestBody Object PathVariable 파라메터 테스트")
void deleteObjectPathVariable() throws Exception {
totalStopWatch.start("DELETE - @RequestBody Object PathVariable 파라메터 테스트");
mockMvc.perform(delete("/pathvariable-object/{seq}", 1)
.contentType(MediaType.APPLICATION_JSON)
.params(TestUtils.objectToMultiValueMap(reqGetSample)))
.andExpect(status().isOk())
.andDo(print());
}
/**
* 모든 테스트 수행 종료시마다 반복적으로 수행됨
*/
@AfterEach
void stop() {
stopWatch.stop();
System.out.println("===================================================");
System.out.println("개별 테스트 성능 측정 결과");
System.out.println("===================================================");
System.out.println("Time Seconds = "+stopWatch.getTotalTimeSeconds()+"s");
System.out.println("Time Millis = "+stopWatch.getTotalTimeMillis()+"ms");
System.out.println("Time Nanos = "+stopWatch.getTotalTimeNanos()+"ns");
//System.out.println(stopWatch.shortSummary());
System.out.println(stopWatch.prettyPrint());
System.out.println("===================================================");
totalStopWatch.stop();
}
/**
* 모든 테스트 종료시 1회 수행
*/
@AfterAll
static void end() {
System.out.println("===================================================");
System.out.println("종합 테스트 성능 측정 결과");
System.out.println("===================================================");
System.out.println("Total Time Seconds = "+totalStopWatch.getTotalTimeSeconds()+"s");
System.out.println("Total Time Millis = "+totalStopWatch.getTotalTimeMillis()+"ms");
System.out.println("Total Time Nanos = "+totalStopWatch.getTotalTimeNanos()+"ns");
//System.out.println(stopWatch.shortSummary());
System.out.println(totalStopWatch.prettyPrint());
System.out.println("===================================================");
}
}
'프로젝트' 카테고리의 다른 글
[Kotlin][SpringBoot Excel] 엑셀 업로드 공통 서비스 가이드 (0) | 2021.08.03 |
---|---|
[Kotlin][SpringBoot Excel] 엑셀 다운로드 공통 서비스 가이드 (1) | 2021.08.03 |
[SpringBoot test] Sample API 및 Sample 객체 코드 (0) | 2020.06.20 |
[SpringBoot test] BaseTest 클래스 사용 가이드 (0) | 2020.06.20 |
[SpringBoot package] 패키지 객체 도메인 가이드 (0) | 2020.06.20 |
댓글