javascript
Spring MVC测试框架入门–第2部分
這個迷你系列的第一個博客介紹了Spring MVC測試框架,并展示了其在單元測試Spring MVC Controller類中作為控制器而不是POJO進行單元測試的用途。 現在是時候討論使用框架進行集成測試了。
 “集成測試”是指將Spring上下文加載到測試環境中,以便控制器可以在“端到端”測試中與合作者一起工作。 
同樣,我將從Spring Social Facebook項目中為FacebookPostsController編寫一個測試,并且正如您所期望的那樣,該測試將是我的FacebookPostsControllerTest類的集成測試版本。 如果需要查看FacebookPostsController代碼或原始的FacebookPostsControllerTest代碼,請查看我的上一個博客 。 有關FacebookPostsController代碼的完整介紹,請參見Spring Social Facebook博客 。
創建集成測試的第一步是將Spring上下文加載到測試環境中。 這是通過在FacebookPostsControllerTest類中添加以下注釋來完成的:
@RunWith ( SpringJUnit4ClassRunner.class )或@ContextConfiguration(“ file-names”)并沒有什么新意,因為它們從Spring 2.5開始就出現了,如果您是Spring開發人員,那么您之前可能已經在集成測試中使用過它們。 新人是@WebAppConfiguration 。
這些批注協同工作以配置您的測試環境。 @RunWith告訴JUnit使用Spring JUnit類運行器運行測試。 @WebAppConfiguration告訴SpringJUnit4ClassRunner集成測試要加載的ApplicationContext應該是WebApplicationContext ,而@ContextConfiguration用于指定加載哪個XML文件以及從何處加載。
 在這種情況下,我正在加載項目的“ servlet-context.xml”和“ data.xml”文件。 “ servlet-context.xml”文件包含Spring Web應用程序所需的所有標準位,例如<annotation-driven />和視圖解析器,而“ data.xml”則包含該應用程序的Spring Social組件。 這里要注意的一點是,我故意使用 
 我想運行端到端集成測試來訪問文件系統,數據庫等的偽生產配置文件。 
這只是示例代碼,在集成測試中通常不會涉及生產數據庫或其他相關資源。 通常,您將配置您的應用程序以訪問集成測試數據庫和其他資源。 解決此問題的一種方法是創建一個測試 XML配置文件。 但是,不要像我在一個項目中看到的那樣,為項目中的每個Maven模塊創建單獨的測試 XML文件。 原因是當您對代碼進行更改時,最終要更改一大堆配置文件才能使集成測試再次正常工作,這既無聊又耗時。 更好的方法是擁有一個版本的XML配置,并使用Spring配置文件為不同的環境配置應用程序。 如果確實選擇使用配置文件,則還需要將@ActiveProfiles(“profile-name”)批注添加到上面列出的其他三個批注中。 但是,這超出了本博客的范圍。
假設您使用的是自動裝配,并且您已經正確設置了<context:component-scan /> ,那么下一步就是將以下實例變量添加到測試類中:
@Autowired private WebApplicationContext wac;這告訴Spring將先前創建的WebApplicationContext注入到測試中。 然后,可以在非常簡單的一行setup()方法中使用它:
@Before public void setUp() throws Exception { mockMvc = MockMvcBuilders.webAppContextSetup(wac).build(); }類似于此測試的“獨立” /“編程”版本, setup()方法的目的是創建一個mockMvc實例,然后使用它來執行測試。 此處的區別在于,它只是通過使用WebApplicationContext作為MockMvcBuilders的參數來MockMvcBuilders 。
整理好setup()方法后,接下來要做的是編寫一個測試,我將從上一個博客中重寫testShowPostsForUser_user_is_not_signed_in()作為集成測試。 令人驚訝的是,該代碼比以前的JUnit版本更簡單:
@Test public void testShowPostsForUser_user_is_not_signed_in() throws Exception { ResultActions resultActions = mockMvc.perform(get("/posts").accept(MediaType.ALL)); resultActions.andExpect(status().isOk()); resultActions.andExpect(view().name("signin")); }如果將此代碼與我以前的博客中的testShowPostsForUser_user_is_not_signed_in()代碼進行比較,您會發現它幾乎相同。 唯一的區別是無需設置任何模擬對象。
 在這一點上,我將演示testShowPostsForUser_user_is_signed_in測試的集成測試版本,但這確實有些棘手。 原因是要掌握他們的Facebook帖子列表,用戶必須登錄其Facebook帳戶,這意味著在正確的必要HttpServletRequest對象之前,需要對服務器進行多次順序調用狀態以方便致電Facebook檢索帖子列表。 對于示例代碼來說,這似乎有點太復雜了,這是我不想在 
 真實的項目。 
我不是將這種復雜性視為Spring MVC測試框架的局限性,而是要強調最佳實踐,這是確保對服務器的調用盡可能獨立且原子。
當然,我可以使用模擬對象或創建虛擬Facebook服務,但是,這超出了本博客的范圍。
一個獨立的一個很好的例子,原子服務器調用是REST調用testConfirmPurchases_selection_1_returns_a_hat(...)用于測試的OrderController從我采取類的Spring MVC,Ajax和JSON第2部分-服務器端代碼博客。 此代碼在Ajax博客中進行了全面描述,它請求購買確認,并以JSON的形式返回。
下面OrderController了返回JSON的OrderController代碼:
/** * Create an order form for user confirmation */ @RequestMapping(value = "/confirm", method = RequestMethod.POST) public @ResponseBody OrderForm confirmPurchases(@ModelAttribute("userSelections") UserSelections userSelections) { logger.debug("Confirming purchases..."); OrderForm orderForm = createOrderForm(userSelections.getSelection()); return orderForm; } private OrderForm createOrderForm(List<String> selections) { List<Item> items = findItemsInCatalogue(selections); String purchaseId = getPurchaseId(); OrderForm orderForm = new OrderForm(items, purchaseId); return orderForm; } private List<Item> findItemsInCatalogue(List<String> selections) { List<Item> items = new ArrayList<Item>(); for (String selection : selections) { Item item = catalogue.findItem(Integer.valueOf(selection)); items.add(item); } return items; } private String getPurchaseId() { return UUID.randomUUID().toString(); }盡管它返回的JSON看起來像這樣:
{"items":[{"id":1,"description":"description","name":"name","price":1.00}, {"id":2,"description":"description2","name":"name2","price":2.00}],"purchaseId":"aabf118e-abe9-4b59-88d2-0b897796c8c0"}下面以冗長的樣式顯示了測試testConfirmPurchases_selection_1_returns_a_hat(...)的代碼。
@Test public void testConfirmPurchases_selection_1_returns_a_hat() throws Exception { final String mediaType = "application/json;charset=UTF-8"; MockHttpServletRequestBuilder postRequest = post("/confirm"); postRequest = postRequest.param("selection", "1"); ResultActions resultActions = mockMvc.perform(postRequest); resultActions.andDo(print()); resultActions.andExpect(content().contentType(mediaType)); resultActions.andExpect(status().isOk()); // See http://goessner.net/articles/JsonPath/ for more on JSONPath ResultMatcher pathMatcher = jsonPath("$items[0].description").value("A nice hat"); resultActions.andExpect(pathMatcher); }上面的代碼不是Spring Guys希望您編寫的代碼; 但是,以冗長的格式更容易討論正在發生的事情。 該方法的結構類似于第1部分中討論的testShowPostsForUser_user_is_signed_in(...)方法。第一步是使用靜態MockMvcRequestBuilders.post(...)方法創建MockHttpServletRequestBuilder類型的postRequest對象。 值"1"的"selection"參數將添加到結果對象中。
然后將postRequest傳遞給mockMvc.perform(...)方法,并返回一個ResultActions對象。
然后使用andExpect(...)方法驗證ResultActions對象,以檢查HTTP狀態(ok = 200)和內容類型為"application/json;charset=UTF-8" 。
此外,我還添加了一個andDo(print())方法調用,以顯示HttpServletRequest和HttpServletResponse對象的狀態。 該調用的輸出如下所示:
MockHttpServletRequest:HTTP Method = POSTRequest URI = /confirmParameters = {selection=[1]}Headers = {}Handler:Type = com.captaindebug.store.OrderControllerMethod = public com.captaindebug.store.beans.OrderForm com.captaindebug.store.OrderController.confirmPurchases(com.captaindebug.store.beans.UserSelections)Resolved Exception:Type = nullModelAndView:View name = nullView = nullModel = nullFlashMap:MockHttpServletResponse:Status = 200Error message = nullHeaders = {Content-Type=[application/json;charset=UTF-8]}Content type = application/json;charset=UTF-8Body = {"items":[{"id":1,"description":"A nice hat","name":"Hat","price":12.34}],"purchaseId":"d1d0eba6-51fa-415f-ac4e-8fa2eaeaaba9"}Forwarded URL = nullRedirected URL = nullCookies = []最后一項測試使用靜態MockMvcResultMatchers.jsonPath(...)檢查"$items[0].description"的JSON路徑的值是否為"A nice hat" 。 為了使用jsonPath(...)靜態方法,您必須在POM.xml中包含JSON Path模塊以解析JSON。
<dependency><groupId>com.jayway.jsonpath</groupId><artifactId>json-path</artifactId><version>0.8.1</version><scope>test</scope></dependency>JSonPath是一種從JSon數據中選擇性提取字段的方法。 它基于XML的XPath思想。
顯然,我上面用過的冗長風格不需要編寫測試。 下面的代碼顯示與Spring的Guy設計的相同代碼:
@Test public void testConfirmPurchases_spring_style() throws Exception { mockMvc.perform(post("/confirm").param("selection", "1")).andDo(print()) .andExpect(content().contentType("application/json;charset=UTF-8")) .andExpect(status().isOk()) .andExpect(jsonPath("$items[0].description").value("A nice hat")); }所以,僅此而已。 概括地說,我們的想法是向您的單元測試中添加適當的注釋,以便Spring加載您的XML配置以創建WebApplicationContext 。 然后將其注入到您的測試中,并在創建mockMvc時作為參數傳遞給Spring MVC Test框架。 然后編寫測試,其想法是將適當構造的MockMvcRequestBuilders對象傳遞給mockMvc.perform(...)方法,然后將其返回值聲明為通過或失敗測試。
 該博客的代碼可在GitHub上找到: https : //github.com/roghughe/captaindebug/在Facebook和Ajax-JSON項目中。 
翻譯自: https://www.javacodegeeks.com/2013/07/getting-started-with-springs-mvc-test-framework-part-2.html
總結
以上是生活随笔為你收集整理的Spring MVC测试框架入门–第2部分的全部內容,希望文章能夠幫你解決所遇到的問題。
                            
                        - 上一篇: Spring MVC控制器的单元测试:配
 - 下一篇: 食品产品备案查询官网(食品产品备案)