SpringBoot MockMvc單元測試入門

1. 介紹

1.1 為什么需要測試

測試是軟件開發(fā)不可或缺的一部分。 在實際應(yīng)用程序中,服務(wù)通常依賴于訪問外部系統(tǒng),因此提供適當(dāng)?shù)母綦x測試非常重要,這樣我們就可以專注于測試給定單元的功能,而無需涉及整個類層次結(jié)構(gòu)。

1.2 Spring解決方案

Spring提供了spring-test包用于測試,在Spring Boot中通過引入spring-boot-starter-test依賴以啟用。Spring提供了三種測試方案來針對不同場景下的測試。

1.2.1 MockMvc

針對Controller的接口測試,我們可以使用MockMvc進行,本節(jié)也將介紹此種框架的使用。

1.2.2 HtmlUnit

HtmlUnit與MockMvc結(jié)合,實現(xiàn)了Html頁面中表單的測試??梢酝ㄟ^獲取某個網(wǎng)頁的內(nèi)容,操作其中的元素,模擬事件。實現(xiàn)針對網(wǎng)頁的測試。

1.2.3 RestTemplate

適用于完全模擬客戶端的請求測試,無須關(guān)心服務(wù)器端的運行(即服務(wù)器端在其他服務(wù)器已經(jīng)運行,本地?zé)o須啟動)。此種方式一般不適用于后端開發(fā)人員的單元測試(因為后端開發(fā)的單元測試其實是在開發(fā)過程中的,必然需要在本地運行服務(wù)端)。

2. MockMvc的使用

2.1 基本組成與流程

MockMvc的幾個重要組件如下:

2.1.1 MockHttpServletRequestBuilder

創(chuàng)建請求,可以設(shè)置參數(shù)、頭信息、編碼、Cookies等基本http請求所含的所有信息。

2.1.2 MockMvc

客戶端,主要入口,執(zhí)行請求。

2.1.3 ResultActions

結(jié)果與動作,MockMvc將MockHttpServletRequestBuilder構(gòu)造的請求發(fā)出后,返回的結(jié)果。并且可以在此基礎(chǔ)上,針對結(jié)果添加一些動作。包含:

  1. andExpect:預(yù)期結(jié)果是否與真實結(jié)果一致
  2. andDo:針對結(jié)果執(zhí)行腳本,常用的為print(),打印結(jié)果
  3. andReturn:獲取結(jié)果
    其中andExpect與andDo返回的類型扔為ResultActions,故可以使用鏈式的方式添加多個動作。

2.1.4 基本流程圖

基本流程

2.2 SpringBoot中的MockMvc使用

SpringBoot中的MockMvc使用相對簡單

2.2.1 編寫Controller接口

@RestController
@RequestMapping("/demo")
public class DemoController {

    @PostMapping("testPost")
    public String testPost(@RequestBody String request) {
        System.out.println(request);
        return "{\"code\":\"0000\"}";
    }

    @GetMapping("/testGet/{id}")
    public String testGet(@PathVariable String id, @RequestParam("name") String name) {
        System.out.println(id);
        System.out.println(name);
        return "{\"code\":\"0000\"}";
    }
}

2.2.2 創(chuàng)建BaseTest

為了簡化代碼,我們創(chuàng)建一個BaseTest來封裝基礎(chǔ)的測試流程。

@RunWith(SpringJUnit4ClassRunner.class)
public abstract class BaseTest {
    @Autowired
    protected WebApplicationContext wac;
    protected MockMvc mockMvc;
    @Before
    public void setup() {
        this.mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();
    }
    public ResultActions buildRequest(Supplier<MockHttpServletRequestBuilder> method) throws Exception {
        String header = getBaseHeader();
        header = header == null ? "" : header;
        return this.mockMvc.perform(method.get().characterEncoding(StandardCharsets.UTF_8.name())
                .contentType(MediaType.APPLICATION_JSON)
                .header(HttpHeaders.AUTHORIZATION, header)).andDo(print()).andExpect(status().is2xxSuccessful());
    }
    public abstract String getBaseHeader();
}

2.2.3 單元測試示例

 @SpringBootTest(classes = TestApplication.class)
public class ApiTest extends BaseTest {
    @Override
    public String getBaseHeader() {
        return null;
    }

    /**
     * get請求測試
     */
    @Test
    public void testGet() throws Exception {
        MockHttpServletRequestBuilder mockHttpServletRequestBuilder = MockMvcRequestBuilders
                .get("/demo/testGet/{id}", 1111);
        mockHttpServletRequestBuilder.param("name", "張三");
        super.buildRequest(() -> mockHttpServletRequestBuilder)
                .andExpect(jsonPath("$.code", is("0000")));
    }

    /**
     * post請求測試
     */
    @Test
    public void testPost() throws Exception {
        MockHttpServletRequestBuilder mockHttpServletRequestBuilder = MockMvcRequestBuilders
                .post("/demo/testPost");
        Map<String,Object> requestMap = new HashMap<>();
        requestMap.put("name","張三");
        mockHttpServletRequestBuilder.content(JSON.toJSONString(requestMap));
        super.buildRequest(() -> mockHttpServletRequestBuilder)
                .andExpect(jsonPath("$.code", is("0000")));
    }
}

2.2.4 JsonPath

JsonPath是一個用于讀取JSON文檔的Java DSL。,
MockMvc引入它用于andExpect中來讀取、比對json結(jié)果。通過表達式讀取文檔,并傳入比對的方法以此進行判斷。
寫法可以是

$.store.book[0].title

$['store']['book'][0]['title']

JsonPath的表達式的語法類似于jquery:

操作符 描述
$ 要查詢的根元素。所有表達式開始元素。
@ 根據(jù)過濾表達式查詢節(jié)點
* 通配符。可在任何名稱或數(shù)字需要的地方使用。
.. 深層掃描??稍谌魏涡枰Q的地方使用。
.<name> 獲取節(jié)點
['<name>' (, '<name>')] 根據(jù)名稱獲取子節(jié)點$['store']['book']
[<number> (, <number>)] 根據(jù)索引獲取子節(jié)點$['store'][0]
[start:end] 獲取從start到end的節(jié)點列表
[?(<expression>)] 過濾表達式。表達式必須求值為布爾值。

JsonPath地址了解更多

快速開發(fā)框架
高質(zhì)量圖片壓縮工具

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容