使用MockMvc進行Junit單元測試
阿新 • • 發佈:2018-11-05
一、單元測試的目的
簡單來說就是在我們增加或者改動一些程式碼以後對所有邏輯的一個檢測,尤其是在我們後期修改後(不論是增加新功能,修改bug),都可以做到重新測試的工作。以減少我們在釋出的時候出現更過甚至是出現之前解決了的問題再次重現。
這裡主要是使用MockMvc對我們的系統的Controller進行單元測試。
對資料庫的操作使用事務實現回滾,及對資料庫的增刪改方法結束後將會還遠資料庫。
二、MockMvc的使用
1、首先我們上一個例子,
import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.junit.Before; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.MediaType; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.transaction.TransactionConfiguration; import org.springframework.test.context.web.WebAppConfiguration; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.RequestBuilder; import org.springframework.test.web.servlet.ResultActions; import org.springframework.test.web.servlet.setup.MockMvcBuilders; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.context.WebApplicationContext; 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.status; /** * Created by zhengcanrui on 16/8/11. */ @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations={"classpath:spring/applicationContext-*xml"}) //配置事務的回滾,對資料庫的增刪改都會回滾,便於測試用例的迴圈利用 @TransactionConfiguration(transactionManager = "transactionManager", defaultRollback = true) @Transactional @WebAppConfiguration public class Test { //記得配置log4j.properties ,的命令列輸出水平是debug protected Log logger= LogFactory.getLog(TestBase.class); protected MockMvc mockMvc; @Autowired protected WebApplicationContext wac; @Before() //這個方法在每個方法執行之前都會執行一遍 public void setup() { mockMvc = MockMvcBuilders.webAppContextSetup(wac).build(); //初始化MockMvc物件 } @org.junit.Test public void getAllCategoryTest() throws Exception { String responseString = mockMvc.perform( get("/categories/getAllCategory") //請求的url,請求的方法是get .contentType(MediaType.APPLICATION_FORM_URLENCODED) //資料的格式 .param("pcode","root") //新增引數 ).andExpect(status().isOk()) //返回的狀態是200 .andDo(print()) //打印出請求和相應的內容 .andReturn().getResponse().getContentAsString(); //將相應的資料轉換為字串 System.out.println("--------返回的json = " + responseString); } }
2、程式碼解釋:
@webappconfiguration是一級註釋,用於宣告一個ApplicationContext整合測試載入WebApplicationContext。作用是模擬ServletContext
@ContextConfiguration:因為controller,component等都是使用註解,需要註解指定spring的配置檔案,掃描相應的配置,將類初始化等。
@TransactionConfiguration(transactionManager = "transactionManager", defaultRollback = true) @Transactional
上面兩句的作用是,讓我們對資料庫的操作會事務回滾,如對資料庫的新增操作,在方法結束之後,會撤銷我們對資料庫的操作。
為什麼要事務回滾?
- 測試過程對資料庫的操作,會產生髒資料,影響我們資料的正確性
- 不方便迴圈測試,即假如這次我們將一個記錄刪除了,下次就無法再進行這個Junit測試了,因為該記錄已經刪除,將會報錯。
- 如果不使用事務回滾,我們需要在程式碼中顯式的對我們的增刪改資料庫操作進行恢復,將多很多和測試無關的程式碼
方法解析:
- perform:執行一個RequestBuilder請求,會自動執行SpringMVC的流程並對映到相應的控制器執行處理;
- get:聲明發送一個get請求的方法。MockHttpServletRequestBuilder get(String urlTemplate, Object... urlVariables):根據uri模板和uri變數值得到一個GET請求方式的。另外提供了其他的請求的方法,如:post、put、delete等。
- param:新增request的引數,如上面傳送請求的時候帶上了了pcode = root的引數。假如使用需要傳送json資料格式的時將不能使用這種方式,可見後面被@ResponseBody註解引數的解決方法
- andExpect:新增ResultMatcher驗證規則,驗證控制器執行完成後結果是否正確(對返回的資料進行的判斷);
- andDo:新增ResultHandler結果處理器,比如除錯時列印結果到控制檯(對返回的資料進行的判斷);
- andReturn:最後返回相應的MvcResult;然後進行自定義驗證/進行下一步的非同步處理(對返回的資料進行的判斷);
-
三、遇到的問題
1、傳送一個被@ResponseBody標識的引數,一直到400錯誤。 即無法傳送一個json格式的資料到Controller層。
解決方法:
MediaType.APPLICATION_JSON
-
SoftInfo softInfo = new SoftInfo(); //設定值 ObjectMapper mapper = new ObjectMapper(); ObjectWriter ow = mapper.writer().withDefaultPrettyPrinter(); java.lang.String requestJson = ow.writeValueAsString(softInfo); String responseString = mockMvc.perform( post("/softs").contentType(MediaType.APPLICATION_JSON).content(requestJson)).andDo(print()) .andExpect(status().isOk()).andReturn().getResponse().getContentAsString();