Junit測試Controller(MockMVC使用),傳輸@RequestBody數據解決辦法

1、單元測試的目的前端

  簡單來講就是在咱們增長或者改動一些代碼之後對全部邏輯的一個檢測,尤爲是在咱們後期修改後(不管是增長新功能,修改bug),均可以作到從新測試的工做。以減小咱們在發佈的時候出現更過甚至是出現以前解決了的問題再次重現。java

  這裏主要是使用MockMvc對咱們的系統的Controller進行單元測試。web

  對數據庫的操做使用事務實現回滾,及對數據庫的增刪改方法結束後將會還遠數據庫。spring

2、MockMvc的使用數據庫

一、首先咱們上一個例子,apache

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);
    }

}

  Spring MVC的測試每每看似比較複雜。其實他的不一樣在於,他須要一個ServletContext來模擬咱們的請求和響應。可是Spring也針對Spring MVC 提供了請求和響應的模擬測試接口,以方便咱們的單元測試覆蓋面不僅是service,dao層。json

二、代碼解釋:spring-mvc

@webappconfiguration是一級註釋,用於聲明一個ApplicationContext集成測試加載WebApplicationContext。做用是模擬ServletContexttomcat

@ContextConfiguration:由於controller,component等都是使用註解,須要註解指定spring的配置文件,掃描相應的配置,將類初始化等。mvc

@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中,將沒法打印日誌。 

三、後臺的返回數據中,最好帶上咱們對數據庫的修改的結果返回的前端。

爲何要在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()))
        ;
    }

3、遇到的問題

一、發送一個被@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中。

二、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>

學習連接:https://www.petrikainulainen.net/spring-mvc-test-tutorial/ 

  致謝:感謝您的閱讀!

相關文章
相關標籤/搜索