====== MVC Test ====== ===== 개요 ===== Spring mvc test framework에서는 API를 통한 Spring MVC 테스트를 지원한다. MVC Test Framework는 내부에서 TestContext를 통해 실제 스프링 구성을 로드하고, 실행 ServletContext를 사용없이 MVC 테스트가 가능하다. \\ ==== Spring mvc server-side test ==== 기존 Spring 3.2버전까지 스프링 MVC Controller를 테스트하는 방법은 Controller를 객체화하거나 객체주입하고 Mock객체(MockHttpServletRequest, MockHttpServletResponse)를 사용하여 단위테스트를 작성하는 것이었다. \\ 그러나 이러한 테스트 방식은 Controller내부에서 쓰이는 많은 annotation기능과 request처리과정의 로직들을 모두 검증/지원하지는 못한다는 단점이 있다. (@initBinder, @ModelAttribute, @ExceptionHandler 등...) Spring 3.2부터는 Spring mvc test framework에서 Spring MVC Test를 보다 쉽고 간편하게 할 수 있는 방법을 제시한다. \\ \\ Spring MVC Test는 "mock"구현을 기반으로하며 서블릿 컨테이너 실행 없이 동작하므로 JSP렌더링을 제외한 Request, Response처리가 동작한다. 물론 forward/redirect가 실제 동작하는 것이 아니며 "forward"또는 "redirect"로 호출된 URL이 저장되며 test코드 내부에서 예상값을 확인해볼 수 있다.\\ \\ Spring MVC Test에서는 JSP를 포함한 Freemarker, Velocity, Thymeleaf와 같은 뷰 타입도 지원하며 HTML, JSON, XML타입의 렌더링 등 다양한 처리방식을 지원하고 있다. \\ \\ Spring MVC Test에 대해 살펴보기에 앞서 테스트 코드 예를 살펴보자. \\ 다음은 JSON요청 사용 예이다. \\ import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; import static org.springframework.test.web.servlet.MockMvcBuilder.*; @RunWith(SpringJUnit4ClassRunner.class) @WebAppConfiguration @ContextConfiguration("test-servlet-context.xml") public class ExampleTests { @Autowired private WebApplicationContext wac; private MockMvc mockMvc; @Before public void setup() { this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build(); } @Test public void getAccount() throws Exception { this.mockMvc.perform(get("/accounts/1").accept(MediaType.parseMediaType("application/json;charset=UTF-8"))) .andExpect(status().isOk()) .andExpect(content().contentType("application/json")) .andExpect(jsonPath("$.name").value("Lee")); } } 위의 코드에서 MockMvc의 perform함수를 통해 "/accounts/1" url로 request를 수행하고 응답받은 response를 통해 상태값(status:200), 컨텐트 타입("application/json"), JSON으로 받은 값들을 확인할 수 있다. \\ === Setup Option === MVC Test의 TestContext는 WebApplicationcontext로 동작한다. \\ Test이전의 setup에서는 Spring mvc test에 필요한 MockMvc의 객체를 가져와야한다. setup option에는 다음 두가지 방법이 있다. == 1. @ContextConfiguration로 설정을 읽어들여 SetUp하는 방법 == @ContextConfiguration의 xml에서 설정을 읽어들이고 WebApplicationContext를 주입하여 mockMvc를 생성한다. @RunWith(SpringJUnit4ClassRunner.class) @WebAppConfiguration @ContextConfiguration("my-servlet-context.xml") public class MyWebTests { @Autowired private WebApplicationContext wac; private MockMvc mockMvc; @Before public void setup() { this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build(); } // ... } == 2. Spring Configuration은 읽지 않고 controller객체만 생성하여 SetUp하는 방법 == Controller가 생성되면서 기본 Spring mvc가 구성된다. public class MyWebTests { private MockMvc mockMvc; @Before public void setup() { this.mockMvc = MockMvcBuilders.standaloneSetup(new AccountController()).build(); } // ... } Web MVC Layer의 구성에 따라 bean들이 호출되기 위해서는 설정 정보가 필요하기 때문에 첫번째 setUp방식을 권장한다. === Static Import === 테스트코드 사용 시, 필요한 API는 Static Import를 선언하여 편리하게 바로 호출해주는 것이 좋다. \\ 예를 들어 MockMvcRequestBuilders.*, MockMvcResultMatchers.*클래스 등과 같이 많이 쓰는 것들은 Static Import로 선언해주도록 한다. import static org.springframework.test.web.server.request.MockMvcRequestBuilders.*; import static org.springframework.test.web.server.result.MockMvcResultMatchers.*; === MVC 테스트 === 다음은 Spring mvc 테스트코드에서 사용 가능한 MockMvc 함수이다. ^함수명^설명^예^ |perform|해당 경로로 요청하며 이때 호출할 URL과 HTTP METHOD를 설정할 수 있다|.perform(get("/account/1")| |param|파라미터를 설정한다.|.param("key", "value")| |cookie|쿠키를 설정한다. |.cookie(new Cookie("key", "value")| |sessionAttr|세션을 설정한다.|sessionAttr("key", "value")| |accept|response를 받을 Accept값을 설정한다.|.accept(MediaType.parseMediaType("application/json;charset=UTF-8")))| |andExpect|예상값의 Assert함수|andExpect(status().isOk()| |andDo|요청/응답에 대한 처리를 한다.|andDo(print())| |andReturn|리턴 처리한다.|.andReturn()| == Perform 함수 처리 예 == request요청처리를 위해 MockMvc의 perform함수를 사용하며 내부에서 MockHttpServletRequest의 값을 설정하여 request요청을 할 수 있다.\\ mockMvc.perform(post("/hotels/{id}", 42).accept(MediaType.APPLICATION_JSON)); HTTP메소드 외에 fileUpload메소드를 통해 내부에서 MockMultipartHttpServletRequest의 객체를 만들어 업로드요청을 수행할 수 있다. \\ mockMvc.perform(fileUpload("/doc").file("a1", "ABC".getBytes("UTF-8"))); URI template에서 Query String 파라미터를 지정할 수도 있다. mockMvc.perform(get("/hotels?foo={foo}", "bar")); 또한 request파라미터를 추가할 수도 있다. mockMvc.perform(get("/hotels").param("foo", "bar")); 요청 URI에서 contextPath와 servletPath는 생략하는 것이 바람직하지만 요청시 Full URI와 함께 테스트해야하는 경우, request매핑이 제대로 작동하도록 contextPath와 servletPath를 설정해주도록 한다. mockMvc.perform(get("/app/main/hotels/{id}").contextPath("/app").servletPath("/main")) 매번 request요청 시 contextPath와 servletPath를 설정하는 것이 번거스러우므로, setup시에 미리 설정하는 것이 편리하다. public class MyWebTests { private MockMvc mockMvc; @Before public void setup() { mockMvc = standaloneSetup(new AccountController()) .defaultRequest(get("/") .contextPath("/app").servletPath("/main") .accept(MediaType.APPLICATION_JSON).build(); } } == andExpect 함수 처리 예 == 예상 결과값을 위해 andExpect함수를 사용하며 하나 이상 사용 가능하다. MockMvcResultMatchers.* 를 static import로 정의하여 andExpect함수 내에서 제공함수를 사용할 수 있다. \\ MockMvcResultMatchers의 제공함수는 다음과 같이 두가지 종류가 있다. \\ * Response의 properties값. (response상태, header, content 등) * 요청처리에서 발생하는 값 (Exception, Model, View, request값, session값 등) response상태 확인 시, mockMvc.perform(get("/accounts/1")).andExpect(status().isOk()); \\ andExpect함수를 여러개 사용 가능 시, mockMvc.perform(post("/persons")) .andExpect(status().isOk()) .andExpect(model().attributeHasErrors("person")); \\ request요청 결과를 출력할 수도 있다. print메소드를 통해 요청 처리시 관련된 모든 결과 데이터를 출력해준다. mockMvc.perform(post("/persons")) .andDo(print()) .andExpect(status().isOk()) .andExpect(model().attributeHasErrors("person")); \\ andReturn메소드를 통해 return결과를 반환할 수도 있다. MvcResult mvcResult = mockMvc.perform(post("/persons")).andExpect(status().isOk()).andReturn(); \\ 같은 예상값을 테스트할 때는 setUp시에 다음과 같이 설정한다. standaloneSetup(new SimpleController()) .alwaysExpect(status().isOk()) .alwaysExpect(content().contentType("application/json;charset=UTF-8")) .build() == addFilters 함수 처리 예 == Filter인스턴스를 두개 이상 등록하고자 할 때, mockMvc 세팅시에 다음과 같이 필터를 추가할 수 있다. mockMvc = standaloneSetup(new PersonController()).addFilters(new CharacterEncodingFilter()).build(); ===== 참고자료 ===== [[http://docs.spring.io/spring/docs/3.2.x/spring-framework-reference/html/testing.html#spring-mvc-test-framework|Spring reference 3.2.x : spring-mvc-test-framework]] \\