介紹 Spring Test MVC HtmlUnit

工程 | Rob Winch | 2014年3月19日 | ...

週一,我宣佈了 Spring Test MVC HtmlUnit 首個里程碑版本的釋出,並承諾會有一個系列部落格來介紹它。這是介紹 Spring Test MVC HtmlUnit 的四部分系列部落格中的第一篇。系列大綱如下所示

為何選擇 Spring Test MockMvc HtmlUnit?

最明顯的問題是:“我為什麼需要這個?”最好的答案是透過探索一個非常基礎的示例應用來找到。假設你有一個 Spring MVC Web 應用,它允許對 Message 物件進行 CRUD 操作。該應用還允許分頁瀏覽所有訊息。你將如何測試它呢?

使用 Spring MVC Test,我們可以輕鬆測試是否能夠建立一個 Message

MockHttpServletRequestBuilder createMessage = post("/messages/")
	.param("summary", "Spring Rocks")
	.param("text", "In case you didn't know, Spring Rocks!");

mockMvc.perform(createMessage)
	.andExpect(status().is3xxRedirection())
	.andExpect(redirectedUrl("/messages/123"));

如果我們想測試用於建立訊息的表單檢視怎麼辦?例如,假設我們的表單看起來像下面的片段

<form id="messageForm" action="/messages/" method="post">
  <div class="pull-right"><a href="/messages/">Messages</a></div>

  <label for="summary">Summary</label>
  <input type="text" class="required" id="summary" name="summary" value="" />

  <label for="text">Message</label>
  <textarea id="text" name="text"></textarea>

  <div class="form-actions">
    <input type="submit" value="Create" />
  </div>
</form>

我們如何確保我們的表單會產生正確的請求來建立新訊息?一種簡單嘗試可能看起來像這樣

mockMvc.perform(get("/messages/form"))
	.andExpect(xpath("//input[@name='summary']").exists())
	.andExpect(xpath("//textarea[@name='text']").exists());

這個測試有一些明顯的問題。如果我們更新控制器使用引數“message”而不是“text”,我們的測試就會錯誤地透過。為了解決這個問題,我們可以結合我們的兩個測試

String summaryParamName = "summary";
String textParamName = "text";
mockMvc.perform(get("/messages/form"))
	.andExpect(xpath("//input[@name='" + summaryParamName + "']").exists())
	.andExpect(xpath("//textarea[@name='" + textParamName + "']").exists());

MockHttpServletRequestBuilder createMessage = post("/messages/")
	.param(summaryParamName, "Spring Rocks")
	.param(textParamName, "In case you didn't know, Spring Rocks!");

mockMvc.perform(createMessage)
	.andExpect(status().is3xxRedirection())
	.andExpect(redirectedUrl("/messages/123"));

這將降低我們的測試錯誤透過的風險,但仍然存在一些問題

  • 如果我們的頁面上有多個表單怎麼辦?誠然,我們可以更新 xpath 表示式,但考慮的因素越多(欄位型別是否正確、欄位是否啟用等),它們就越複雜。
  • 另一個問題是,我們正在做比預期多一倍的工作。我們必須先驗證檢視,然後使用剛剛驗證過的相同引數提交檢視。理想情況下,這可以一次完成。
  • 最後,有些事情我們仍然無法考慮。例如,如果表單有 JavaScript 驗證,我們也希望進行驗證怎麼辦?

總體問題是,測試一個網頁不是單一的互動。相反,它是使用者如何與網頁互動以及該網頁如何與其他資源互動的組合。例如,表單檢視的結果被用作使用者建立訊息的輸入。另一個例子是我們的表單檢視利用了額外的資源,比如 JavaScript 驗證,這些資源會影響頁面的行為。

整合測試來救援?

為了解決上述問題,我們可以進行整合測試,但這有一些明顯的缺點。考慮測試允許我們分頁瀏覽訊息的檢視。我們可能需要以下測試

  • 當訊息為空時,我們的頁面是否向用戶顯示沒有結果可用的訊息?
  • 我們的頁面是否正確顯示單條訊息?
  • 我們的頁面是否正確支援分頁?

為了設定這些測試,我們需要確保資料庫中包含正確的訊息。這導致了一些問題

  • 確保資料庫中有正確的訊息可能很繁瑣(考慮可能的外部索引鍵)。
  • 測試會很慢,因為每個測試都需要確保資料庫處於正確狀態。
  • 由於我們的資料庫需要處於特定狀態,我們無法並行執行測試。
  • 對自動生成的 id、時間戳等進行斷言可能很困難。

這些問題並不意味著我們應該完全放棄整合測試。相反,我們可以透過將詳細的測試轉移到使用 mock 服務來減少整合測試的數量,這將顯著提高測試速度。然後,我們可以使用較少的整合測試來驗證簡單的流程,以確保一切正常協同工作。

Spring Test MVC HtmlUnit 登場

那麼,如何在測試頁面互動的同時仍然獲得高效能呢?我相信你已經猜到了……Spring Test MVC HtmlUnit 將使我們能夠

  • 使用我們已用於整合測試的工具(即 HtmlUnit, WebDriver, & Geb)輕鬆測試我們的頁面,而無需啟動應用伺服器
  • 支援 JavaScript 測試
  • 選擇使用 mock 服務來加速測試。

注意:與 Spring MVC Test 一樣,HtmlUnit 整合將適用於不依賴於 Servlet 容器的模板技術(例如 Thymeleaf, Freemarker, Velocity 等)。它不適用於 JSP,因為 JSP 依賴於 Servlet 容器。

接下來...

我希望這篇文章能讓你對我的下一篇文章感到興奮,該文章將討論如何透過整合 Spring Test MVC 和 HtmlUnit 來解決其中的一些問題。


請提供反饋!

如果您對本系列部落格或 Spring Test MVC HtmlUnit 有任何反饋,我鼓勵您透過 github issues 聯絡我,或在 twitter @rob_winch 上 ping 我。當然,最好的反饋是以貢獻的形式出現。

獲取 Spring 時事通訊

訂閱 Spring 時事通訊,保持聯絡

訂閱

搶先一步

VMware 提供培訓和認證,助力您加速發展。

瞭解更多

獲取支援

Tanzu Spring 透過一份簡單的訂閱提供 OpenJDK™、Spring 和 Apache Tomcat® 的支援和二進位制檔案。

瞭解更多

近期活動

檢視 Spring 社群的所有近期活動。

檢視全部