Loading...
Spring Framework Reference Documentation 7.0.2의 Why HtmlUnit Integration?의 한국어 번역본입니다.
아래의 경우에 피드백에서 신고해주신다면 반영하겠습니다.
감사합니다 :)
가장 먼저 떠오르는 질문은 “왜 이것이 필요한가?”일 것입니다. 그에 대한 답은 아주 기본적인 sample 애플리케이션을 살펴보면 가장 잘 알 수 있습니다. CRUD operation을 지원하는 Message 객체에 대한 Spring MVC web 애플리케이션이 있다고 가정해 봅시다. 이 애플리케이션은 모든 message에 대한 paging도 지원합니다.
이를 어떻게 testing 하겠습니까?
Spring MVC Test를 사용하면 다음과 같이 Message를 생성할 수 있는지 쉽게 test할 수 있습니다:
1MockHttpServletRequestBuilder createMessage = post("/messages/") 2 .param("summary", "Spring Rocks") 3 .param("text", "In case you didn't know, Spring Rocks!"); 4 5mockMvc.perform(createMessage) 6 .andExpect(status().is3xxRedirection()) 7 .andExpect(redirectedUrl("/messages/123"));
1@Test 2fun test() { 3 mockMvc.post("/messages/") { 4 param("summary", "Spring Rocks") 5 param("text", "In case you didn't know, Spring Rocks!") 6 }.andExpect { 7 status().is3xxRedirection() 8 redirectedUrl("/messages/123") 9 } 10}
message를 생성할 수 있도록 해주는 form view를 test하고 싶다면 어떻게 해야 할까요? 예를 들어, 우리의 form이 다음 snippet과 같다고 가정해 봅시다:
1<form id="messageForm" action="/messages/" method="post"> 2 <div class="pull-right"><a href="/messages/">Messages</a></div> 3 4 <label for="summary">Summary</label> 5 <input type="text" class="required" id="summary" name="summary" value="" /> 6 7 <label for="text">Message</label> 8 <textarea id="text" name="text"></textarea> 9 10 <div class="form-actions"> 11 <input type="submit" value="Create" /> 12 </div> 13</form>
우리의 form이 새로운 message를 생성하기 위해 올바른 request를 생성하는지 어떻게 확인할 수 있을까요? 단순한 시도는 다음과 비슷할 수 있습니다:
1mockMvc.perform(get("/messages/form")) 2 .andExpect(xpath("//input[@name='summary']").exists()) 3 .andExpect(xpath("//textarea[@name='text']").exists());
1mockMvc.get("/messages/form").andExpect { 2 xpath("//input[@name='summary']") { exists() } 3 xpath("//textarea[@name='text']") { exists() } 4}
이 test에는 몇 가지 명백한 단점이 있습니다. controller를 update하여 parameter로 text 대신 message를 사용하도록 변경해도, HTML form이 controller와 동기화되어 있지 않음에도 우리의 form test는 계속 통과합니다.
이를 해결하기 위해 다음과 같이 두 개의 test를 결합할 수 있습니다:
1String summaryParamName = "summary"; 2String textParamName = "text"; 3mockMvc.perform(get("/messages/form")) 4 .andExpect(xpath("//input[@name='" + summaryParamName + "']").exists()) 5 .andExpect(xpath("//textarea[@name='" + textParamName + "']").exists()); 6 7MockHttpServletRequestBuilder createMessage = post("/messages/") 8 .param(summaryParamName, "Spring Rocks") 9 .param(textParamName, "In case you didn't know, Spring Rocks!"); 10 11mockMvc.perform(createMessage) 12 .andExpect(status().is3xxRedirection()) 13 .andExpect(redirectedUrl("/messages/123"));
1val summaryParamName = "summary"; 2val textParamName = "text"; 3mockMvc.get("/messages/form").andExpect { 4 xpath("//input[@name='$summaryParamName']") { exists() } 5 xpath("//textarea[@name='$textParamName']") { exists() } 6} 7mockMvc.post("/messages/") { 8 param(summaryParamName, "Spring Rocks") 9 param(textParamName, "In case you didn't know, Spring Rocks!") 10}.andExpect { 11 status().is3xxRedirection() 12 redirectedUrl("/messages/123") 13}
이렇게 하면 test가 잘못 통과할 위험을 줄일 수 있지만, 여전히 몇 가지 문제가 남아 있습니다:
전반적인 문제는 web page를 testing하는 것이 단일한 interaction만을 포함하지 않는다는 점입니다. 대신, 사용자가 web page와 어떻게 상호작용하는지와 그 web page가 다른 resource와 어떻게 상호작용하는지의 조합입니다.
예를 들어, form view의 결과는 message를 생성하기 위한 사용자 input으로 사용됩니다. 또한, form view는 JavaScript validation과 같이 page의 동작에 영향을 주는 추가 resource를 사용할 수도 있습니다.
위에서 언급한 문제를 해결하기 위해 end-to-end integration testing을 수행할 수 있지만, 이에도 몇 가지 단점이 있습니다. message를 paging할 수 있게 해주는 view를 testing하는 경우를 생각해 봅시다. 다음과 같은 test가 필요할 수 있습니다:
이러한 test를 설정하려면 database에 올바른 message가 포함되어 있는지 확인해야 합니다. 이는 다음과 같은 여러 추가적인 challenge로 이어집니다:
이러한 challenge가 end-to-end integration testing을 완전히 포기해야 한다는 뜻은 아닙니다. 대신, 자세한 test를 더 빠르고, 더 신뢰할 수 있으며, side effect가 없는 mock 서비스를 사용하도록 refactoring하여 end-to-end integration test의 수를 줄일 수 있습니다.
그런 다음 모든 것이 함께 제대로 동작하는지 확인하기 위해 간단한 workflow를 검증하는 소수의 진정한 end-to-end integration test를 구현할 수 있습니다.
그렇다면 page 간의 interaction을 testing하는 것과 test suite 내에서 좋은 성능을 유지하는 것 사이의 균형을 어떻게 이룰 수 있을까요? 그에 대한 답은 “MockMvc를 HtmlUnit과 통합하는 것”입니다.
MockMvc를 HtmlUnit과 통합하려고 할 때 선택할 수 있는 option은 여러 가지가 있습니다:
HtmlUnit Integration
MockMvc and HtmlUnit