頁面物件
2013 年 9 月 10 日
當您對網頁撰寫測試時,您需要參照該網頁中的元素,才能按一下連結並確定顯示的內容。不過,如果您撰寫直接操作 HTML 元素的測試,您的測試將會對 UI 變更很脆弱。頁面物件會以特定於應用程式的 API 包裝 HTML 頁面或片段,讓您可以在不深入探究 HTML 的情況下操作頁面元素。

頁面物件的基本經驗法則,在於它應該允許軟體用戶端執行人類可以執行或查看的任何操作。它也應該提供一個易於編寫程式且隱藏視窗中底層小工具的介面。因此,若要存取文字欄位,您應該有存取方法,這些方法會接收並傳回字串;核取方塊應該使用布林值;按鈕應該以動作導向的方法名稱表示。頁面物件應該封裝在 gui 控制項本身中尋找和操作資料所需的機制。一個良好的經驗法則,在於想像變更具體控制項,在這種情況下,頁面物件介面不應該變更。
儘管有「頁面」物件一詞,這些物件通常不應該針對每個頁面建置,而應該針對頁面上的重要元素建置 [1]。因此,顯示多個相簿的頁面將會有一個包含多個相簿頁面物件的相簿清單頁面物件。可能還會有標頭頁面物件和頁尾頁面物件。話雖如此,複雜 UI 的部分階層只存在於結構化 UI,頁面物件不應該揭露此類複合結構。經驗法則,在於對應用程式使用者有意義的頁面中建模結構。
類似地,如果您導覽至另一個頁面,初始頁面物件應該為新頁面傳回另一個頁面物件 [2]。一般而言,頁面物件操作應該傳回基本類型(字串、日期)或其他頁面物件。
對於頁面物件是否應該包含斷言本身,或僅提供資料供測試指令碼執行斷言,有不同的意見。主張在頁面物件中包含斷言的支持者表示,這有助於避免在測試指令碼中重複斷言,讓提供更好的錯誤訊息變得更容易,並支援更多 TellDontAsk 樣式 API。主張無斷言頁面物件的支持者表示,包含斷言會將提供存取頁面資料的責任與斷言邏輯混在一起,並導致頁面物件過於龐大。
我贊成在頁面物件中沒有斷言。我認為您可以透過提供用於常見斷言的斷言函式庫來避免重複,這也可以讓提供良好的診斷變得更容易。 [3]
頁面物件通常用於測試,但本身不應進行斷言。它們的責任是提供對底層頁面狀態的存取。由測試用戶端執行斷言邏輯。
我已根據 HTML 說明此模式,但相同的模式同樣適用於任何 UI 技術。我已看到此模式有效地用於隱藏 Java swing UI 的詳細資料,我毫不懷疑它也已廣泛用於幾乎所有其他 UI 架構。
並行問題是頁面物件可以封裝的另一個主題。這可能涉及隱藏非同步作業中的非同步性,這些作業對使用者而言並非非同步。它也可能涉及封裝 UI 架構中的執行緒問題,在這些架構中,您必須擔心在 UI 和工作執行緒之間分配行為。
頁面物件最常使用於測試,但也可以用於提供應用程式頂端的指令碼介面。通常最好將指令碼介面放在 UI 下方,這通常較不複雜且較快。然而,對於將太多行為放入 UI 的應用程式,使用頁面物件可能會讓糟糕的工作變得更好。(但如果可以,請試著移動該邏輯,這將對指令碼和 UI 的長期健全性都有好處。)
使用某種形式的 DomainSpecificLanguage(例如 Cucumber 或內部 DSL)來撰寫測試是很常見的。如果您這樣做,最好將測試 DSL 分層在頁面物件上,以便您有解析器將 DSL 陳述句轉換為對頁面物件的呼叫。
如果您在測試方法中使用 WebDriver API,您做錯了。-- Simon Stewart。
旨在將邏輯移出 UI 元素的模式(例如 簡報模式、監督控制器 和 被動檢視)會降低透過 UI 進行測試的實用性,進而減少對頁面物件的需求。
頁面物件是封裝的經典範例 - 它們會對其他元件(測試)隱藏 UI 結構和元件小工具的詳細資訊。在開發時尋找類似這樣的狀況是一個良好的設計原則 - 問問自己「我如何才能對軟體的其他部分隱藏某些詳細資訊?」與任何封裝一樣,這會產生兩個好處。我已經強調過,透過將操控 UI 的邏輯限制在單一位置,你可以在不影響系統中其他元件的情況下修改它。一個後續的好處是,它讓客戶端(測試)程式碼更容易理解,因為那裡的邏輯是關於測試的意圖,而不是被 UI 詳細資訊弄得雜亂無章。
延伸讀物
我最初在 視窗驅動程式 的名稱下描述這個模式。然而,自那時起,「頁面物件」一詞已由 Selenium 網路測試架構普及化,並成為普遍使用的名稱。
Selenium 的 wiki 強烈建議使用頁面物件,並提供如何使用它們的建議。它也偏好無斷言的頁面物件。
一個團隊在軟體升級後,測量更新兩組 Selenium 測試套件的時間。他們發現使用頁面物件的版本在第一個測試案例中花費的時間稍長,但在其他測試案例中則快得多。如需更多詳細資訊,請參閱 Leotta 等人,「使用頁面物件模式改善測試套件的可維護性」,ICSTW 2013
致謝
Perryn Fowler、Pete Hodgson 和 Simon Stewart 對這篇文章的草稿提供了特別有用的意見 - 儘管像往常一樣,我非常感謝 Thoughtworks 內部軟體開發清單中的各種居民,感謝他們的建議和更正。備註
1: 這裡有一個論點認為「頁面物件」這個名稱具有誤導性,因為它會讓你認為每個頁面只應該有一個頁面物件。像「面板物件」之類的名稱會更好,但「頁面物件」這個術語已經被接受了。這也是為什麼命名是 TwoHardThings 之一的原因的另一個說明。
2: 讓頁面物件負責在回應導航等事項時建立其他頁面物件是常見的建議。然而,有些從業人員比較喜歡讓頁面物件傳回一些通用的瀏覽器內容,而測試會根據測試流程(特別是有條件的流程)來控制要在該內容之上建立哪些頁面物件。他們的偏好基於測試腳本知道接下來預期的頁面,而這個知識不需要在頁面物件本身中重複。當使用通常會在型別簽章中顯示頁面導航的靜態型別語言時,他們會更偏好這種方式。
3: 即使像我這樣通常偏好不宣告型別的風格的人,也認為一種宣告型別的形式很好。這些宣告型別是檢查頁面或應用程式在這個時間點的不變式,而不是測試正在探查的特定事項。