본문 바로가기

내가 당면한 문제와 해결방안

springboot test using cucumber

1. 설정 및 사전 작업

 

intellij에서 뭐 cucumber도 플러그인 추가해준것 같기도

 

<dependency>
    <groupId>io.cucumber</groupId>
    <artifactId>cucumber-java8</artifactId>
    <version>4.3.0</version>
    <scope>test</scope>
</dependency>

<dependency>
    <groupId>io.cucumber</groupId>
    <artifactId>cucumber-junit</artifactId>
    <version>4.3.0</version>
    <scope>test</scope>
</dependency>

maven 리포지토리에서 https://mvnrepository.com/search?q=cucumber 를 검색해서 필요한 것을 설치해주긔. 설치가 안되어서 노트북 깨부시고 싶었는디(컴맹의 행동양식) intellij의 .idea 폴더와 .iml 파일을 모두 지우고 && 로컬의 ~/.m2/repository/io/cucumber 다 지우고 재설치 하니까 되었음.

 

 

하단을 보면 cucumber가 보임. 진작 이럴것이지 
src/test/resources 경로에 거킨(.feature파일)을 위치시키고

 

 

시나리오를 이렇게 작성해본다. 근데 영어로 쓰는게 나을지 한글으로 쓰는게 나을지. 아직 잘 모르겠다.

한글으로 쓰고 싶으면 

https://cucumber.io/docs/gherkin/reference/#overview

여기를 참고해서 쓰면 됨. 거킨은 작은 오이라고 한다 ㅋㅋ 

 

 

 

(수정중)

 

 

---> 결국 영어로 작성하기로 했다 '-' 그런거지 뭐.

 

 

 

 


 

 

 

 

 

디렉토리 구조를.. 다시 바꾸었다. //ㅅ // 

 

 

 

 

일케 되어있구

 

 

 

regionController.feature를 맨첨에 작성해줬다.

Feature: Region Controller Test
  Scenario: /api/v1/continents 대륙 목록
    When User wants to get list of continents

  Scenario: /api/v1/continents/count 대륙 수
    When User wants to get count of continents

  Scenario: /api/v1/countries 국가 목록
    When User wants to get list of countries

  Scenario: /api/v1/countries/{country_code} 국가 정보
    When User wants to get information of country, especially "KR"

  Scenario: /api/v1/countries/count 국가 수
    When User wants to get count of countries

  Scenario: /api/v1/region_only/count 전체 지역 수 (대륙, 국가 제외)
    When User wants to get the total number of regions except continent and country

  Scenario: /api/v1/regions 지역 목록
    When User wants to get list of regions, query is "Al" page is 1 pageSize is 20

  Scenario: /api/v1/regions 지역 추가
    When User wants to post region,

 

이걸 작성해주면 When 부분에 밑줄이 생기면서 "너 이거 시나리오만 있고 테스트케이스는 작성 안되어있어" 일케 알려준다. 

 

어 그럼 만들면 되지

 

package 패키지명;

import cucumber.api.CucumberOptions;
import cucumber.api.junit.Cucumber;
import org.junit.runner.RunWith;

@RunWith(Cucumber.class)
@CucumberOptions(features = "src/test/resources")
public class CucumberTest {
}

 

package 패키지명;

import org.springframework.boot.test.context.SpringBootContextLoader;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.web.WebAppConfiguration;

@ContextConfiguration(
        classes = Application.class,
        loader = SpringBootContextLoader.class)
@WebAppConfiguration
//@IntegrationTest
public class SpringIntegrationTest {
}

 

이 두개를 만들고

 

package 패키지명;

import cucumber.api.java.en.When;

import org.hamcrest.Matchers;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureWebMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.annotation.Bean;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;

import java.io.IOException;

import static org.hamcrest.Matchers.*;
import static org.mockito.Mockito.*;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;


public class RegionControllerStepDefs extends SpringIntegrationTest {

    private static final Logger log = LoggerFactory.getLogger(RegionControllerStepDefs.class);

    @Autowired
    WebApplicationContext webApplicationContext;

    @Bean
    MockMvc mockMvc() {
        return MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
    }


    @When("User wants to get list of continents")
    public void userGetListOfContinents() throws Exception {
        mockMvc().perform(get("/api/v1/continents"))
                .andExpect(status().is2xxSuccessful())
                .andDo(print());
        log.info("대륙 목록 ");
    }

    @When("User wants to get count of continents")
    public void userWantsToGetCountOfContinents() throws Exception {
        mockMvc().perform(get("/api/v1/continents/count"))
                .andExpect(status().is2xxSuccessful())
                .andDo(print())
                .andExpect(jsonPath("$.data", is(notNullValue())));
        log.info("대륙 수");
    }

    @When("User wants to get list of countries")
    public void userWantsToGetListOfCountries() throws Exception {
        mockMvc().perform(get("/api/v1/countries"))
                .andExpect(status().is2xxSuccessful())
                .andDo(print())
                .andExpect(jsonPath("$.data", is(notNullValue())));
        log.info("국가 목록");
    }

    @When("User wants to get information of country, especially {string}")
    public void userWantsToGetInformationOfCountryEspecially(String country_code) throws Exception {
        mockMvc().perform(get("/api/v1/countries/" + country_code))
                .andExpect(status().is2xxSuccessful())
                .andDo(print())
                .andExpect(jsonPath("$.data", is(notNullValue())))
                .andExpect(jsonPath("$.data").isArray())
                .andExpect(jsonPath("$.data[0].class_type").value("REGION"));
//                .andExpect(jsonPath("$.data[0].id", is(notNullValue())));
        log.info("국가 정보");
    }

    @When("User wants to get count of countries")
    public void userWantsToGetCountOfCountries() throws Exception {
        mockMvc().perform(get("/api/v1/countries/count" ))
                .andExpect(status().is2xxSuccessful())
                .andDo(print())
                .andExpect(jsonPath("$.data", is(notNullValue())))
                .andExpect(jsonPath("$.data.count").isNumber());
        log.info("국가 수");
    }

    @When("User wants to get the total number of regions except continent and country")
    public void userWantsToGetTheTotalNumberOfRegionsExceptContinentAndCountry() throws Exception {
        mockMvc().perform(get("/api/v1/region_only/count" ))
                .andExpect(status().is2xxSuccessful())
                .andDo(print())
                .andExpect(jsonPath("$.data", is(notNullValue())))
                .andExpect(jsonPath("$.data.count").isNumber());
        log.info("전체 지역 수 (대륙, 국가 제외)");
    }

    @When("User wants to get list of regions, query is {string} page is {int} pageSize is {int}")
    public void userWantsToGetListOfRegionsQueryIsPageIsPageSizeIs(String query, int page, int pageSize) throws Exception {

        mockMvc().perform(get("/api/v1/regions?q=" + query + "&page=" + page + "&pageSize=" + pageSize ))
                .andExpect(status().is2xxSuccessful())
                .andDo(print())
                .andExpect(jsonPath("$.data", is(notNullValue())))
                .andExpect(jsonPath("$.data.total_count").isNumber())
                .andExpect(jsonPath("$.data.items").isArray())
                .andExpect(jsonPath("$.data.items[?(@.class_type == '" + REGION + "')]").exists());

        log.info("지역 목록");
    }
}

 

테스트케이스가 잘 실행이 된 모습

 

 

 

 

 

 

결론 : 테스트 케이스 작성이나 시나리오 작성이 어려웠다기보다 (원래 그것이 어렵고 고민되어야하는 부분이었을 텐데)

ApplicationContext나 Bean 의존성 주입. IoC 제어 역전 과 같은 부분이 이해가 되지 않아서 테스트를 실행할때 어려움을 많이 겪었다. 아직도 만족할 만큼 이해가 된건 아닌것같아서 좀더 찾아보고 하단에 수정하기로.

 

 

 


 

 

 

참고

스프링 현재버전 테스팅 doc (https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-testing.html)

스프링 테스팅 unittest 와 integrationtest과 그에 사용되는 annotation에 대한 doc (https://docs.spring.io/spring/docs/5.1.7.RELEASE/spring-framework-reference/testing.html#testing)

 

큐컴버 프레임 워크 (https://cucumber.io/docs)

큐컴버 예제 1 (https://thepracticaldeveloper.com/2018/03/31/cucumber-tests-spring-boot-dependency-injection/)

큐컴버 예제 2 (https://www.baeldung.com/cucumber-spring-integration )

큐컴버 예제 3 (https://www.blazemeter.com/blog/api-testing-with-cucumber-bdd-configuration-tips)

 

MockMvc를 사용한 유닛테스트 예제 (https://www.mkyong.com/spring-boot/spring-test-how-to-test-a-json-array-in-jsonpath/)

MockMvc를 사용, MockHttpServletResponse 의 json을 체크할 때. regex 몰라서 stackoverflow에 물어봄 (https://stackoverflow.com/questions/56253443/jsonpath-expression-expected-value-but-return-list-of-values/56259498?noredirect=1#comment99162008_56259498)

 

 

 

(책)스프링부트를 활용한 마이크로서비스 개발

 

 

(dependency injection)

- cucumber.class 로 Runwith 했을 때 dependency injection이 안되었던 것 해결 한 답변 : https://stackoverflow.com/questions/38836337/cucumber-with-spring-boot-1-4-dependencies-not-injected-when-using-springboott

- 이번에 bean개념과 dependency injection이 이해가 되지 않았었는데.. 그와 관련되어 jvm이해에도 도움을 주는 쉬운 자료 https://www.slideshare.net/hnki0104/bueatiful-jvm-world

- dependency injection 과 제어역전의 의미를 쉽게 설명 : ( https://medium.com/@jang.wangsu/di-dependency-injection-%EC%9D%B4%EB%9E%80-1b12fdefec4f)