標籤:ext 管理員 更新 pac web pos time app .json
一、單元測試的目的
簡單來說就是在我們增加或者改動一些代碼以後對所有邏輯的一個檢測,尤其是在我們後期修改後(不論是增加新功能,修改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@WebAppConfigurationpublic 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); }}
Spring MVC的測試往往看似比較複雜。其實他的不同在於,他需要一個ServletContext來類比我們的請求和響應。但是Spring也針對Spring MVC 提供了請求和響應的類比測試介面,以方便我們的單元測試覆蓋面不只是service,dao層。
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;然後進行自訂驗證/進行下一步的非同步處理(對返回的資料進行的判斷);
注意事項:
- 在mac上使用log4j是,假如使用了${catalina.home}需要注意,mac不會去找到tomcat所在的路徑,直接回到根路徑 “/”,而正常情況下,根路徑是沒有寫入權限的,需要使用管理員賦許可權。
- log4j在配置完成之後,需要設定起列印日誌的層級,假如沒有設定,在Junit中,將無法列印日誌。
3、背景返回資料中,最好帶上我們對資料庫的修改的結果返回的前端。
為什麼要在data中返回一個修改或者添加的對象
- 將資料返回給前端,前端容易判斷資料是否添加或者修改成功
- 更新或者添加完資料經常需要重新整理頁面,將資料直接給了前端,前端不用再發一個請求來擷取
- 單元測試的時候,能對資料庫的DDL(增刪改)操作的時候,我們能對資料進行審核,從何判斷我們的操作是否是成功的。如下面的例子:
我們發送一個添加操作,添加一個SoftInfo對象,SoftInfo類定義如下:
public class SoftInfo { private String id; private String name;}
添加完之後,由於我們進行了單元測試的交易回復,我們將不能再資料庫中看我們我們的的添加操作,無法判斷操作是否成功。
為瞭解決上面的問題,我們可以在返回的json的資料中添加一個“data”欄位,解析該json中的data欄位資料,判斷我們的添加操作是否成功的。json格式如下:
{ "status":200, "data":{"id":"2","name":"測試"}}
我們可以使用andExpect方法對返回的資料進行判斷,用“$.屬性”擷取裡面的資料,如我要擷取返回資料中的"data.name",可以寫成"$.data.name"。下面的例子是判斷返回的data.name=“測試”。
@Test public void testCreateSeewoAccountUser() throws Exception { mockMvc.perform(post("/users") .contentType(MediaType.APPLICATION_FORM_URLENCODED) ).andExpect(status().isOk()) .andExpect(jsonPath("$.data.name", is("測試")))) .andExpect(jsonPath("$.data.createTime", notNullValue())) ; }
三、遇到的問題
1、發送一個被@ResponseBody標識的參數,一直到400錯誤。 即無法發送一個json格式的資料到Controller層。
解決方案1:
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();
解決方案2:使用com.alibaba.fastjson.JSONObject將對象轉換為Json資料
SoftInfo softInfo = new SoftInfo();//。。。設定值 String requestJson = JSONObject.toJSONString(folderInfo); String responseString = mockMvc.perform( post("/softs").contentType(MediaType.APPLICATION_JSON).content(requestJson)).andDo(print()) .andExpect(status().isOk()).andReturn().getResponse().getContentAsString();
注意上面contentType需要設定成MediaType.APPLICATION_JSON,即聲明是發送“application/json”格式的資料。使用content方法,將轉換的json資料放到request的body中。
2、java.lang.NoClassDefFoundError: com/jayway/jsonpath/InvalidPathException
缺少了jar包:
可以添加一下的maven依賴
<dependency> <groupId>com.jayway.jsonpath</groupId> <artifactId>json-path</artifactId> <version>0.8.1</version> <scope>test</scope> </dependency> <dependency> <groupId>com.jayway.jsonpath</groupId> <artifactId>json-path-assert</artifactId> <version>0.8.1</version> <scope>test</scope> </dependency>
Junit測試Controller(MockMVC使用),傳輸@RequestBody資料解決辦法