Spring Test MVC HtmlUnit 簡介

工程 (Engineering) | 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"));

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

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

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

整合測試來救援?

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

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

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

  • 確保正確的訊息在資料庫中可能很繁瑣(想想可能的外鍵)。
  • 測試會很慢,因為每個測試都需要確保資料庫處於正確的狀態。
  • 由於我們的資料庫需要處於特定狀態,因此我們無法並行運行測試。
  • 對自動產生的 ID、時間戳記等進行斷言可能具有挑戰性。

這些問題並不意味著我們應該完全放棄整合測試。 相反,我們可以通過將我們的詳細測試轉移到使用模擬服務來減少整合測試的數量,這樣可以更快地執行。 然後,我們可以使用更少的整合測試來驗證簡單的工作流程,以確保一切正常協同工作。

進入 Spring Test MVC HtmlUnit

那麼,我們如何才能在測試頁面的互動和仍然獲得效能之間取得平衡? 我相信您已經猜到了... Spring Test MVC HtmlUnit 將允許我們:

  • 使用我們已經用於整合測試的工具(即 HtmlUnit、WebDriver 和 Geb)輕鬆地測試我們的頁面,而無需啟動應用程式伺服器。
  • 支援 JavaScript 的測試
  • 選擇性地使用模擬服務進行測試,以加快測試速度。

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

下一步...

我希望這篇文章能激發您對我的下一篇文章的興趣,該文章討論了我們如何通過整合 Spring Test MVC 和 HtmlUnit 來解決其中一些問題。


請提供回饋!

如果您對這個部落格系列或 Spring Test MVC HtmlUnit 有任何回饋意見,我鼓勵您通過 github 問題與我們聯繫,或在 twitter 上 ping 我 @rob_winch。 當然,最好的回饋來自於 貢獻

獲取 Spring 電子報

隨時關注 Spring 電子報

訂閱

領先一步

VMware 提供培訓和認證,以加速您的進度。

了解更多

獲得支援

Tanzu Spring 在一個簡單的訂閱中提供對 OpenJDK™、Spring 和 Apache Tomcat® 的支援和二進位檔案。

了解更多

即將到來的活動

查看 Spring 社群中所有即將到來的活動。

查看所有