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 다 지우고 재설치 하니까 되었음.
시나리오를 이렇게 작성해본다. 근데 영어로 쓰는게 나을지 한글으로 쓰는게 나을지. 아직 잘 모르겠다.
한글으로 쓰고 싶으면
여기를 참고해서 쓰면 됨. 거킨은 작은 오이라고 한다 ㅋㅋ
(수정중)
---> 결국 영어로 작성하기로 했다 '-' 그런거지 뭐.
디렉토리 구조를.. 다시 바꾸었다. //ㅅ //
일케 되어있구
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)
'내가 당면한 문제와 해결방안' 카테고리의 다른 글
javascript this (0) | 2019.06.29 |
---|---|
python 동기 -> 비동기 싱글스레드 -> 비동기 멀티스레드 (0) | 2019.06.27 |
can not instantiate from JSON object (missing default constructor or creator, or perhaps need to add/enable type information?) (0) | 2019.05.14 |
for...in / for... of loop diff - javascript (0) | 2019.04.20 |
2 > /dev/null 의 의미 (0) | 2019.03.14 |