프로젝트

[SpringBoot test] Sample API 테스트 코드

nineDeveloper 2020. 6. 20.
728x90

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는 이미 BaseTestprotected 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 테스트"); 와 같이 totalStopWatchstart 시킨다

@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("===================================================");
    }

}
728x90

댓글

💲 추천 글