SpringBoot MockMvc單元測試入門

1. 介紹

1.1 為什么需要測試

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

1.2 Spring解決方案

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

1.2.1 MockMvc

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

1.2.2 HtmlUnit

HtmlUnit與MockMvc結(jié)合享潜,實(shí)現(xiàn)了Html頁面中表單的測試剑按±绞酰可以通過獲取某個(gè)網(wǎng)頁的內(nèi)容鸟废,操作其中的元素,模擬事件盒延。實(shí)現(xiàn)針對(duì)網(wǎng)頁的測試兰英。

1.2.3 RestTemplate

適用于完全模擬客戶端的請(qǐng)求測試畦贸,無須關(guān)心服務(wù)器端的運(yùn)行(即服務(wù)器端在其他服務(wù)器已經(jīng)運(yùn)行,本地?zé)o須啟動(dòng))趋厉。此種方式一般不適用于后端開發(fā)人員的單元測試(因?yàn)楹蠖碎_發(fā)的單元測試其實(shí)是在開發(fā)過程中的君账,必然需要在本地運(yùn)行服務(wù)端)沈善。

2. MockMvc的使用

2.1 基本組成與流程

MockMvc的幾個(gè)重要組件如下:

2.1.1 MockHttpServletRequestBuilder

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

2.1.2 MockMvc

客戶端猜极,主要入口魔吐,執(zhí)行請(qǐng)求莱找。

2.1.3 ResultActions

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

  1. andExpect:預(yù)期結(jié)果是否與真實(shí)結(jié)果一致
  2. andDo:針對(duì)結(jié)果執(zhí)行腳本建蹄,常用的為print(),打印結(jié)果
  3. andReturn:獲取結(jié)果
    其中andExpect與andDo返回的類型扔為ResultActions痛单,故可以使用鏈?zhǔn)降姆绞教砑佣鄠€(gè)動(dòng)作劲腿。

2.1.4 基本流程圖

基本流程

2.2 SpringBoot中的MockMvc使用

SpringBoot中的MockMvc使用相對(duì)簡單

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)建一個(gè)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請(qǐng)求測試
     */
    @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請(qǐng)求測試
     */
    @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是一個(gè)用于讀取JSON文檔的Java DSL。嫌松,
MockMvc引入它用于andExpect中來讀取奕污、比對(duì)json結(jié)果。通過表達(dá)式讀取文檔贾陷,并傳入比對(duì)的方法以此進(jìn)行判斷髓废。
寫法可以是

$.store.book[0].title

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

JsonPath的表達(dá)式的語法類似于jquery:

操作符 描述
$ 要查詢的根元素慌洪。所有表達(dá)式開始元素凑保。
@ 根據(jù)過濾表達(dá)式查詢節(jié)點(diǎn)
* 通配符欧引。可在任何名稱或數(shù)字需要的地方使用憋肖。
.. 深層掃描。可在任何需要名稱的地方使用租副。
.<name> 獲取節(jié)點(diǎn)
['<name>' (, '<name>')] 根據(jù)名稱獲取子節(jié)點(diǎn)$['store']['book']
[<number> (, <number>)] 根據(jù)索引獲取子節(jié)點(diǎn)$['store'][0]
[start:end] 獲取從start到end的節(jié)點(diǎn)列表
[?(<expression>)] 過濾表達(dá)式用僧。表達(dá)式必須求值為布爾值责循。

JsonPath地址了解更多

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末院仿,一起剝皮案震驚了整個(gè)濱河市歹垫,隨后出現(xiàn)的幾起案子排惨,更是在濱河造成了極大的恐慌暮芭,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,284評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件畜晰,死亡現(xiàn)場離奇詭異舷蟀,居然都是意外死亡野宜,警方通過查閱死者的電腦和手機(jī)匈子,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,115評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門虎敦,熙熙樓的掌柜王于貴愁眉苦臉地迎上來其徙,“玉大人唾那,你說我怎么就攤上這事∑谌” “怎么了龟虎?”我有些...
    開封第一講書人閱讀 164,614評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵鲤妥,是天一觀的道長拱雏。 經(jīng)常有香客問我旭斥,道長,這世上最難降的妖魔是什么古涧? 我笑而不...
    開封第一講書人閱讀 58,671評(píng)論 1 293
  • 正文 為了忘掉前任垂券,我火速辦了婚禮,結(jié)果婚禮上羡滑,老公的妹妹穿的比我還像新娘菇爪。我一直安慰自己,他們只是感情好柒昏,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,699評(píng)論 6 392
  • 文/花漫 我一把揭開白布凳宙。 她就那樣靜靜地躺著,像睡著了一般职祷。 火紅的嫁衣襯著肌膚如雪氏涩。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,562評(píng)論 1 305
  • 那天是尖,我揣著相機(jī)與錄音痰催,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛扫皱,可吹牛的內(nèi)容都是我干的胎食。 我是一名探鬼主播厕怜,決...
    沈念sama閱讀 40,309評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼递雀,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了滤奈?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,223評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤伪嫁,失蹤者是張志新(化名)和其女友劉穎针史,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體频祝,經(jīng)...
    沈念sama閱讀 45,668評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡漓糙,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,859評(píng)論 3 336
  • 正文 我和宋清朗相戀三年蝇庭,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片纹因。...
    茶點(diǎn)故事閱讀 39,981評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖陕截,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情形葬,我是刑警寧澤冻辩,帶...
    沈念sama閱讀 35,705評(píng)論 5 347
  • 正文 年R本政府宣布咙咽,位于F島的核電站互亮,受9級(jí)特大地震影響炊昆,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,310評(píng)論 3 330
  • 文/蒙蒙 一逢倍、第九天 我趴在偏房一處隱蔽的房頂上張望挚币。 院中可真熱鬧激捏,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,904評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽秉沼。三九已至敞咧,卻和暖如春丰包,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背矩动。 一陣腳步聲響...
    開封第一講書人閱讀 33,023評(píng)論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,146評(píng)論 3 370
  • 正文 我出身青樓苟翻,卻偏偏與公主長得像诅炉,于是被迫代替她去往敵國和親父款。 傳聞我的和親對(duì)象是個(gè)殘疾皇子瞻坝,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,933評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容