Spring Web MVC框架(十一) Spring Web MVC測試框架

Spring 也提供了完善的測試框架,我們可以方便的測試Spring Web MVC應(yīng)用程序。為了使用這個測試框架,我們需要添加它的依賴項。

compile group: 'org.springframework', name: 'spring-test', version: '4.3.6.RELEASE'

服務(wù)端測試

我們可以利用Spring提供的Mock對象來測試我們Spring程序的服務(wù)端行為。通過這些Mock對象,我們可以建立一個假的服務(wù)器,然后發(fā)送一些假的請求,來測試我們的程序。為了能簡潔的編寫測試代碼,我們最好在代碼中使用靜態(tài)導(dǎo)入將MockMvcRequestBuilders.*、MockMvcResultMatchers.*MockMvcBuilders.*引入到代碼中。

建立測試環(huán)境

建立Spring Web MVC的測試環(huán)境和普通的Spring 單元測試略有不同。我們需要使用@WebAppConfiguration注解測試類。Spring知道這是一個Web MVC測試之后,就會使用@ContextConfiguration注解中的配置文件來創(chuàng)建一個WebApplicationContext,然后我們可以將其注入到測試類中。然后要做的事情就是創(chuàng)建MockMvc對象,我們大部分測試都要通過該對象進(jìn)行。

@RunWith(SpringRunner.class)
@WebAppConfiguration
@ContextConfiguration("file:src/main/webapp/WEB-INF/applicationContext.xml")
public class UserControllerTest {
    @Autowired
    private WebApplicationContext context;

    private MockMvc mvc;

    @Before
    public void init() {
        mvc = MockMvcBuilders.webAppContextSetup(context).build();
    }
}

當(dāng)然,如果只需要測試某個控制器,我們完全可以不加載完整的配置文件。這時候可以使用MockMvcBuilders.standaloneSetup來僅使用Spring默認(rèn)配置配置某個控制器。

public class SimpleTests {

    private MockMvc mockMvc;

    @Before
    public void setup() {
        this.mockMvc = MockMvcBuilders.standaloneSetup(new AccountController()).build();
    }

}

發(fā)起請求

這里假定代碼中已經(jīng)靜態(tài)導(dǎo)入上面提到的一些類。

我們使用MockMvc的perform方法發(fā)起一個HTTP請求,這個請求可以是get、post等,然后我們還可以為請求設(shè)置accept等信息。

mockMvc.perform(post("/users/{id}", 42).accept(MediaType.ALL));

當(dāng)然也可以發(fā)起文件上傳請求。

mockMvc.perform(fileUpload("/upload").file("file", file.getBytes("UTF-8")));

我們可以直接在請求中包含參數(shù)。

mockMvc.perform(get("/users?user={foo}", "bar"));

也可以使用param方法傳遞參數(shù),這種方式可以傳遞POST表單數(shù)據(jù)。

mockMvc.perform(post("/users").param("foo", "bar"));

如有需要,我們還可以為請求添加contextPath和servletPath。

mockMvc.perform(get("/myproject/contextpath/users").contextPath("/myproject").servletPath("/contextpath"))

期望結(jié)果

發(fā)起請求之后,我們需要驗證請求是否正確處理。這時候需要在perform方法之后再調(diào)用andExpect方法。我們可以期望獲得各種結(jié)果,最常用的就是獲得各種響應(yīng)碼。下面的例子期望首頁可以正常訪問。當(dāng)然status()方法也提供了其他了響應(yīng)碼方法來滿足我們的需求。

mockMvc.perform(get("/index")).andExpect(status().isOk());

還可以期望結(jié)果的媒體類型。

mvc.perform(get("/users.xml"))
        .andExpect(status().isOk())
        .andExpect(content().contentType(MediaType.APPLICATION_XML));

有時候需要驗證請求返回的模型,比如下面就斷言結(jié)果會有錯誤。

mockMvc.perform(post("/updateInfo"))
    .andExpect(status().isOk())
    .andExpect(model().attributeHasErrors("user"));

某些情況下需要查看請求或響應(yīng)的內(nèi)容。我們可以調(diào)用Spring提供的print或log方法來打印信息或者記錄日志。默認(rèn)情況下print方法會將結(jié)果輸出到System.out,而log方法會將日志記錄到調(diào)試級別的org.springframework.test.web.servlet.result包下。

mockMvc.perform(post("/updateInfo"))
    .andExpect(status().isOk())
    .andDo(print())
    .andExpect(model().attributeHasErrors("user"));

有時候需要詳細(xì)檢驗返回結(jié)果。我們可以在所有期望方法的最后添加andReturn方法。該方法會返回一個MvcResult對象,我們可以調(diào)用該對象的各種get方法獲取我們需要的信息。

MvcResult mvcResult = mockMvc.perform(post("/listUsers")).andExpect(status().isOk()).andReturn();

如果某些期望是所有方法都需要的,我們可以將它設(shè)置為共用的。但是一旦設(shè)置就無法更改。所以如果我們不需要某個共用期望的話就只能創(chuàng)建一個新的MockMvc對象了。

standaloneSetup(new UserController())
    .alwaysExpect(status().isOk())
    .alwaysExpect(content().contentType("application/json;charset=UTF-8"))
    .build()

如果我們希望在單個控制器中添加過濾器的話,可以在建立MockMvc對象的時候指定過濾器。

mockMvc = standaloneSetup(new UserController()).addFilters(new CharacterEncodingFilter()).build();

spring-mvc-showcase是一個Spring官方開發(fā)的示例程序,包含了Spring Web MVC的例子和基本功能,也包含了所有的服務(wù)端測試代碼。這也是一個很好的學(xué)習(xí)資源。

HtmlUnit集成

MockMvc雖然好用,但是畢竟是一個假的測試,它沒有實際運(yùn)行的服務(wù)器, 也不會進(jìn)行實際的視圖渲染、轉(zhuǎn)發(fā)和重定向等操作。如果我們希望測試實際的HTML視圖、JavaScript驗證等功能,就需要使用HtmlUnit。

我們需要在項目中引用HtmlUnit的依賴。

compile group: 'net.sourceforge.htmlunit', name: 'htmlunit', version: '2.24'

然后初始化一個WebClient。

@Autowired
WebApplicationContext context;

WebClient webClient;

@Before
public void setup() {
    webClient = MockMvcWebClientBuilder
        .webAppContextSetup(context)
        .build();
}

這樣配置的話,默認(rèn)所有localhost下的請求就會自動通過MockMvc對象來訪問,不需要實際HTTP連接,這方便我們本機(jī)測試。而其他域名會正常使用網(wǎng)絡(luò)來連接,這可以讓我們測試CDN等的狀況。

然后我們可以使用WebClient來創(chuàng)建測試了。這里我直接貼Spring文檔里的例子了。我們從例子中可以看到,WebClient的使用方法和使用普通的JavaScript操作DOM差不多。下面是創(chuàng)建請求的代碼。

HtmlForm form = createMsgFormPage.getHtmlElementById("messageForm");
HtmlTextInput summaryInput = createMsgFormPage.getHtmlElementById("summary");
summaryInput.setValueAttribute("Spring Rocks");
HtmlTextArea textInput = createMsgFormPage.getHtmlElementById("text");
textInput.setText("In case you didn't know, Spring Rocks!");
HtmlSubmitInput submit = form.getOneHtmlElementByAttribute("input", "type", "submit");
HtmlPage newMessagePage = submit.click();

下面是執(zhí)行驗證的代碼。這里的斷言使用了AssertJ庫。

assertThat(newMessagePage.getUrl().toString()).endsWith("/messages/123");
String id = newMessagePage.getHtmlElementById("id").getTextContent();
assertThat(id).isEqualTo("123");
String summary = newMessagePage.getHtmlElementById("summary").getTextContent();
assertThat(summary).isEqualTo("Spring Rocks");
String text = newMessagePage.getHtmlElementById("text").getTextContent();
assertThat(text).isEqualTo("In case you didn't know, Spring Rocks!");

從這里我們就可以看到直接使用HtmlUnit的缺點了,那就是代碼笨重,不好看。Spring還提供了另外兩個類庫WebDriver和Geb來簡化HtmlUnit的測試過程,詳見Spring 參考文檔 HtmlUnit集成

客戶端的REST測試

如果需要客戶端測試REST程序,Spring也提供了相關(guān)功能。直接來看Spring官方的例子。我們需要先創(chuàng)建一個RestTemplate對象,然后創(chuàng)建MockRestServiceServer并綁定到RestTemplate上。然后使用MockRestServiceServer的expect方法發(fā)起請求并測試結(jié)果。最后調(diào)用verify方法驗證是否滿足所有期望。這種方式不需要啟動實際服務(wù)器,效率很高。

RestTemplate restTemplate = new RestTemplate();

MockRestServiceServer mockServer = MockRestServiceServer.bindTo(restTemplate).build();
mockServer.expect(requestTo("/greeting")).andRespond(withSuccess());

// 使用RestTemplate進(jìn)行其他測試 ...

mockServer.verify();

客戶端測試也可以和服務(wù)端測試結(jié)合起來。我們可以利用MockMvc對象來創(chuàng)建RestTemplate,這樣就會使用服務(wù)端的邏輯來測試代碼而不需要啟動實際服務(wù)器。

MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
this.restTemplate = new RestTemplate(new MockMvcClientHttpRequestFactory(mockMvc));

// 使用RestTemplate進(jìn)行其他測試 ...

mockServer.verify();

參考資料

Spring 參考文檔 15.6. Spring MVC Test Framework

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,546評論 6 533
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,570評論 3 418
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 176,505評論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,017評論 1 313
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 71,786評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,219評論 1 324
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,287評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,438評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,971評論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 40,796評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,995評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,540評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,230評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,662評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,918評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,697評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 47,991評論 2 374

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

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 134,785評論 18 139
  • Spring Boot 參考指南 介紹 轉(zhuǎn)載自:https://www.gitbook.com/book/qbgb...
    毛宇鵬閱讀 46,898評論 6 342
  • 本章內(nèi)容: 映射請求到Spring控制器 透明地綁定表單參數(shù) 校驗表單提交 狀態(tài)管理、工作流以及驗證都是Web 開...
    謝隨安閱讀 8,611評論 0 4
  • 主要內(nèi)容 將web請求映射到Spring控制器 綁定form參數(shù) 驗證表單提交的參數(shù) 寫在前面:關(guān)于Java We...
    程序熊大閱讀 9,035評論 15 73
  • 人這一生,不知道能走多遠(yuǎn),也不知道能走多久,更不知道在哪里會停下來……我不知道在過去幾十年里,我是在沙上走過的...
    尼丫閱讀 174評論 0 0