spring에서는 3.2버전부터 MockMvc를 지원하여 매우 편리하게 컨트롤러 테스트 코드를 작성할 수 있었다.
spring boot에서는 spring-boot-starter-test 모듈에 일반적으로 spring-test 모듈과 같이 사용하는 hamcrest, mokito-core, json-path등의 라이브러리가 같이 모듈화 되어 조금 더 편리하게 테스트를 할 수 있다.
이번 포스팅에서는 간단한 상품 정보를 조회하는 데모 어플리케이션을 이용하여 스프링 부트에서 테스트를 진행하는 방법을 알아보겠다.
먼저 메이븐을 사용한다면 pom.xml에 spring-boot-starter-test 모듈을 추가하자.
spring-boot-starter-test 모듈에는 spring-test모듈 외에 hamcrest, ockito-core, json-path등의 테스트에 유용한 라이브러리가 미리 포함되어 있다.
<메이븐 디펜던시>
org.springframework.boot spring-boot-starter-test test
아래는 컨트롤러 코드이다. url path에 상품id를 넣어 상품정보를 조회한다.
<컨트롤러 코드>
package com.example.demo.controller; import com.example.demo.model.Product; import com.example.demo.service.ProductService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/") public class ProductController { @Autowired private ProductService productService; @RequestMapping("/products/{productId}") public Product getProduct(@PathVariable(value = "productId") Long productId){ return productService.getProduct(productId); } }
서비스 코드에서는 파라미터로 넘어온 id에 대한 상품을 반환한다. 데모코드이기 때문에 임의로 Product 객체를 생성하여 반환한다.
<서비스 코드>
package com.example.demo.service; import com.example.demo.model.Product; import org.springframework.stereotype.Service; @Service public class ProductService { public Product getProduct(Long productId) { Product product = new Product(); product.setProductId(productId); product.setProductName("과자"); product.setDescription("맛있는 과자"); product.setPrice(105.5); return product; } }
아래는 Product에 해당하는 모델 객체이다. 상품id, 이름, 설명, 가격필드를 가지고 있다.
<모델 코드>
package com.example.demo.model; public class Product { private Long productId; private String productName; private String description; private double price; public Long getProductId() { return productId; } public void setProductId(Long productId) { this.productId = productId; } public String getProductName() { return productName; } public void setProductName(String productName) { this.productName = productName; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } public double getPrice() { return price; } public void setPrice(double price) { this.price = price; } }
다음은 테스트 코드를 작성해보겠다.
package com.example.demo; import com.example.demo.controller.ProductController; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.http.MediaType; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.result.MockMvcResultHandlers; import org.springframework.test.web.servlet.setup.MockMvcBuilders; import org.springframework.web.context.WebApplicationContext; import static org.hamcrest.Matchers.is; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; @RunWith(SpringRunner.class) @SpringBootTest public class DemoTestApplicationTests { @Autowired private WebApplicationContext wac; private MockMvc mockMvc; @Before public void setUp() { mockMvc = MockMvcBuilders.webAppContextSetup(wac).build(); } @Test public void testGetProduct() throws Exception { mockMvc.perform(get("/products/1") .accept(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()) .andExpect(handler().handlerType(ProductController.class)) .andExpect(handler().methodName("getProduct")) .andExpect(jsonPath("$.productId", is(1))) .andExpect(jsonPath("$.productName", is("과자"))) .andExpect(jsonPath("$.price", is(105.5))) .andDo(MockMvcResultHandlers.print()); } }
위의 테스트 코드를 간단하게 설명하면 다음과 같다.
첫번째로 junit의 @Before 어노테이션을 이용하여 mockMvc 객체를 생성한다.
그리고 @Test 어노테이션이 달려있는 메소드에서 실제 실행할 테스트를 작성한다.
각각의 메소드를 설명하면 아래와 같다.
mockMvc.perform : 실제 요청을 수행한다.
get("products/1") : 호출할 URL를 기술한다.
andExpect : 요청에 대한 각종 기대값을 설정한다.
andDo : 요청에 대한 처리를 진행한다.
위의 코드에서는 get 요청을 하기 위한 url을 설정하고 실행되는 컨트롤러 클래스와 메소드 그리고 jsonPath 라이브러리를 이용하여 결과 값에 대한 검증을 수행하고 있다.
그리고 마지막으로 andDo 메소드에서는 요청에 대한 처리를 진행한다. print 메소드를 이용하여 결과를 출력한다.
<테스트 수행 결과>
MockHttpServletRequest: HTTP Method = GET Request URI = /products/1 Parameters = {} Headers = {Accept=[application/json]} Handler: Type = com.example.demo.controller.ProductController Method = public com.example.demo.model.Product com.example.demo.controller.ProductController.getProduct(java.lang.Long) Async: Async started = false Async result = null Resolved Exception: Type = null ModelAndView: View name = null View = null Model = null FlashMap: Attributes = null MockHttpServletResponse: Status = 200 Error message = null Headers = {Content-Type=[application/json;charset=UTF-8]} Content type = application/json;charset=UTF-8 Body = {"productId":1,"productName":"과자","description":"맛있는 과자","price":105.5} Forwarded URL = null Redirected URL = null Cookies = []
수행 결과를 보면 기대한 값에 맞게 요청이 처리된 점을 확인 할 수 있고 그 외에 자세한 호출 정보를 확인 할 수 있다.
'Java & Spring' 카테고리의 다른 글
spring + JWT + Angular 프로젝트 (0) - 프로젝트 시작 (0) | 2017.09.25 |
---|---|
[java] Collections.emptyList와 Collections.emptyMap (0) | 2017.07.13 |
[Java] 자바 8 Default method 정리 (0) | 2017.04.17 |
자바 Map을 Json으로 변환시 키로 정렬하기 (0) | 2017.03.11 |
mysql에서 batch insert가 작동하지 않는 경우 해결 방법 (0) | 2017.03.11 |