1. 程式人生 > 其它 >秒懂自動化測試中的PO模式

秒懂自動化測試中的PO模式

自動化測試在軟體專案團隊中的作用舉足輕重,眾所周知合理地展開自動化測試,可以有效降低錯誤修復成本,並對軟體質量保證(QA)過程起到全面推進的作用。

以web自動化為例,在Selenium框架的輔助下,自動化測試工程師們編寫web自動化測試指令碼似乎是一項較簡單的任務,但在專案進展過程中,尤其是迭代頻繁的敏捷專案研發模式下,測試人員不經意而實現的那些低質量自動化測試指令碼往往會引發後期高昂的程式碼維護成本,久而久之所謂的自動化測試在你的團隊中就形同虛設了。

回想那些年被UI自動化坑過的橋段歷歷在目,當遇上敏捷專案中“短頻快”迭代交付時,你的自動化測試指令碼很可能成為團隊成本開銷最高昂的任務之一。

試想更改50個測試指令碼中所依賴的某web頁面元素(也許就1個元素)將涉及到至少50+個自動化測試指令碼的更新,更新指令碼的同時不可避免會出現一些其他連鎖問題(不知道用蝴蝶效應來比喻這類糟糕的情況是否合適)。這項工程不僅耗時長,而且對於早期自動化測試在團隊中的推廣與實踐也是一個嚴重的阻礙因素。

都說良好的開端是成功的一半,自動化測試的落地亦是如此,在指令碼實現之前,我們需要有個相對精良的設計,面對避無可避的UI元素變更,除了將那些變更頻度不高的迴歸測試進行自動化,還有什麼方式能夠降低我們日後對於指令碼開銷帶來的高昂成本呢?如何避免一個元素定位變更導致“牽一髮而動全身”呢?我們是否能對變更進行限制,只動一個地方,並且讓每個與之相關的測試指令碼都能使用它?回答是肯定的,相信大家不難得出PO模式可以幫我們落實。

下面我們就來聊一聊Selenium中如何使用頁面物件模型(PO —— Page Object)來實現可維護和可重用的自動化測試指令碼,讓你秒懂PO在UI自動化中的價值。
1

Selenium模擬Github自動化登入流程

這裡我們先以Github (http://6tt.co/rV8V)為例,結合selenium自動化實現模擬登入:


完整登入流程程式碼如下:


為了覆蓋更多webdriver中的定位方式,這裡以ID, NAME,CSS_SELECTOR,XPATH分別對使用者名稱文字框,密碼框,登入按鈕,以及登入後頁面上顯示的“Start a project”按鈕進行定位;

可以發現如果以上四個頁面元素,但凡有一個發生變化,我們需要對該指令碼進行更新,如果這些定位發生變化的元素同時還存在於其他自動化指令碼檔案中,我們需要依次更新每個牽連其中的指令碼檔案,就整個專案而言,指令碼更新所花費的開銷還是相當大的。

2

Selenium+unittest實現Github自動化登入測試指令碼

還是以上指令碼和登入場景,既然是自動化測試,我們就結合python自帶的unittest框架,進行自動化測試用例指令碼編寫,完整程式碼如下:


從指令碼中可見,我們通過unittest把登入業務單獨封裝成了一個test_login()方法,即單獨的測試用例,通過assertEqual進行斷言判斷登入後是否出現預期的資訊:


同樣,如果某個控制元件位置發生了變化,所有用到這個控制元件的測試用例指令碼檔案都需要進行更新,耦合度越高程式碼維護成本越高,耗費的時間精力指數之高更是無從計量。
我們迫切需要合理的解決方案 —— PO模式來重構自動測試指令碼的層次。

3

PO頁面物件模型

為什麼需要頁面物件模型(PO)

指令碼維護的主要問題是,如果100個不同的指令碼使用相同的頁面元素,只要對該元素做了任何更改,就需要更改所有指令碼,耗時且容易出錯。
一種用於指令碼維護更好的方法是為每一個自動化測試所需的web頁面建立一個單獨的類檔案,用於查詢、填充或驗證該頁面中的web元素。任何用到該web頁面元素的指令碼都可以通過呼叫這個類。如果該web頁面元素髮生了變化,我們只需要在其對應的類檔案中進行更改即可,而不必在100個不同的指令碼中逐一更新。這種方法就是頁面物件模型(POM),它有助於提高程式碼的可讀性、可維護性和可重用性。

PO的實現

頁面物件模型(PO)的基本結構:將單個頁面上所有Web元素和在這些Web元素上操作的方法都放在一個Page類檔案中,而用於驗證的測試用例需要單獨作為測試方法的一部分。
我們還是以github登入流程為例,結合頁面物件模型(PO)實現自動化測試,首先我們需要將登入頁面作為一個類將其封裝,並單獨提煉出該頁面中需要用到的元素(類的成員屬性),把對所需元素的操作封裝成獨立的方法(類的成員方法):


在Login Page類中,我們需要用到頁面url, 使用者名稱輸入框,密碼框,登入按鈕,登陸後Button“Start a project”,所以該類共有5個成員


該類需要一個初始化方法,其他操作有:開啟頁面,使用者名稱輸入,密碼輸入,登入按鈕點選,登入後“Start a project”按鈕文字提取,所以Login Page類的成員方法有:


同時為了做到測試指令碼與頁面元素物件的分離,針對每個頁面進行的測試用例指令碼我們也單獨存放,BaseTest指令碼檔案中僅僅執行通用的操作:


針對每一個功能業務流測試,單獨進行測試指令碼的編寫:

完整版分層程式碼實現如下:【PO模式下Selenium + unittest實現Github自動化登入測試指令碼】

由此可見,無論login頁面元素髮生任何變化,我們需要修改的僅僅只是Login Page類,除此之外其餘指令碼均不受影響,頁面物件模型大大降低了自動化指令碼的維護成本及因指令碼更新引發的出錯概率。

4

框架的優化

在PO模式協助下,被測頁面中任意元素定位發生變更都能輕而易舉地解決自動化指令碼的更新問題。到此是不是說明大功告成了呢?是否還存在可優化的地方呢?答案是肯定的,PO模式讓我們僅需要在Page頁面類單個檔案中對變化的元素定位進行更新,那是否儘可能使任何一個自動化指令碼檔案都不發生變更呢,即便是Page頁面類本身?
很簡單,只需要把這些定位作為資料單獨存放在配置檔案中(配置檔案的型別可以是csv, excel,txt),通常csv檔案較為常見。
下面我們把Login Page中需要定位的元素單獨放到如下csv檔案中,login_page.csv檔案的第一行是標題,用來對每個欄位做描述,以下每一行代表一組資料,不同欄位資料間隔用逗號區分(當然用空格也行,只不過csv檔案預設逗號間隔):

以第二行為例,“url”見名知意,表示這行是url相關的資料,“http://6tt.co/rV8V”就是url的值;
第三行中“username_id”表示這樣用來存放username的定位,通過id來定位,“login_field就是username的id定位值(可參見我們在login page中寫的程式碼),下面幾行是同樣的含義。如此我們就把元素/定位/定位後對應的值從python指令碼檔案中獨立出來了。
對於上面的“login_page.py”PO類檔案則更改如下,通過讀取csv配置檔案中的內容,對login page中的成員屬性進行賦值,而不必將這些屬性值硬編碼在python檔案中,這樣一來即便哪個元素的定位發生變化,我們只需更改csv配置檔案即可:

更改後完整版的login_page.py原始碼如下:

將配置資料單獨放在檔案中,很容易讓我們聯想到可以將測試資料也從測試用例方法中剝離出來,即如果我們有多組待測賬號,也可以通過同樣的方式做到資料與測試指令碼的分離,也就是我們經常提到的DDT資料驅動測試,這部分優化大家可以後續自行嘗試,實現方式和以上實現配置檔案讀取方式雷同,這裡就不展開了。

5

PO的優勢

  • 面對象模型提倡將單個頁面元素的操作,元素間組成的業務流,以及操作後的驗證,三者互相獨立。這樣可以使我們的程式碼更清晰、更容易理解;

  • 頁面物件獨立於測試用例,這樣我們就可以根據專案需求,用不同的測試框架作用於相同的頁面物件。例如,我們可以將PO與Unittest / Pytest / TestNG / Junit整合在一起進行WEB功能測試,也可以和JBehave/Cucumber整合在一起進行驗收測試;

  • 由於PO類中的方法具有可重性,這就大大減少了程式碼冗餘,程式碼變得更少、更優化;

  • 通過將PO類中定義的方法名更具真實意義,我們可以很容易地將UI中發生的操作與方法名對映在一起。例如,如果在點選按鈕後我們到達了使用者資訊面,方法可以命名為'gotoUserInfo()';


100%完美設計模型不存在,PO模式也會有自己的限制,但針對web自動化不失為一個較好的解決問題方案,最後我們以思維導圖總結本次分享的核心技術點: