為UI頁面寫測試用例時(比如web頁面,移動端頁面),測試用例會存在大量元素和操作細節。當UI變化時,測試用例也要跟著變化, PageObject 很好的解決了這個問題!

使用UI自動化測試工具時(包括selenium,appium等),如果無統一模式進行規範,隨著用例的增多會變得難以維護,而 PageObject 讓自動化指令碼井井有序,將 page 單獨維護並封裝細節,可以使 testcase 更穩健,不需要大改大動。

使用

具體做法:把元素資訊和操作細節封裝到Page類中,在測試用例上呼叫Page物件(PageObject),比如存在一個功能“選取相簿標題”,需要為之建立函式selectAblumWithTitle(),函式內部是操作細節findElementsWithClass(‘album’)等:

不懂PO 設計模式?這篇實戰文帶你搞定 PO

以選“取相簿標題”舉例,虛擬碼如下:

selectAblumWithTitle() {

#選取相簿

findElementsWithClass(‘album’)

#選取相簿標題

findElementsWithClass(‘title-field’)

#返回標題內容

return getText()

}

page object的主要原則是提供一個簡單介面 (或者函式,比如上述的selectAblumWithTitle),讓呼叫者在頁面上可以做任何操作,點選頁面元素,在輸入框輸入內容等。

因此,如果要訪問一個文字欄位,page object應該有獲取和返回字串的方法。page object應該封裝對資料的操作細節,比如查詢元素和點選元素。當頁面元素改動時,應該只改變page類中的內容,不需要改變呼叫它的地方。

不要為每個UI頁面都建立一個page類,應該只為頁面中重要的元素建立page類。

比如,一個頁面顯示多個相簿,應該建立一個相簿列表page object,它包含許多相簿page object。如果某些複雜UI的層次結構只是用來組織UI,那麼它就不應該出現在page object中。page object的目的是透過給頁面建模,從而對應用程式的使用者變得有意義:

不懂PO 設計模式?這篇實戰文帶你搞定 PO

如果你想導航到另一個頁面,初始page物件應當return另一個page物件,比如點選註冊,進入註冊頁面,在程式碼中就應該return Register()。如果想獲取頁面資訊,可以return基本型別(字串、日期)。

建議不要在page object中放斷言。應該去測page object,而不是讓page object自己測自己,page object的責任是提供頁面的狀態資訊。這裡僅用HTML描述Page Object,這種模式還可以用來隱藏Java swing UI細節,它可用於所有UI框架。

PageObject 原則

PageObject的核心思想是六大原則,掌握六大原則才可以進行 PageObject 實戰演練,這是 PageObject的精髓所在。

selenium官方凝聚出六大原則,後面的PageObject使用都將圍繞六大原則開展:

公共方法代表頁面提供的服務

不要暴露頁面細節

不要把斷言和操作細節混用

方法可以return到新開啟的頁面

不要把整頁內容都放到PO中

相同的行為會產生不同的結果,可以封裝不同結果

下面,對上述六大原則進行解釋:

原則一:要封裝頁面中的功能(或者服務),比如點選頁面中的元素,可以進入到新的頁面,於是,可以為這個服務封裝方法“進入新頁面”。

原則二:封裝細節,對外只提供方法名(或者介面)。

原則三:封裝的操作細節中不要使用斷言,把斷言放到單獨的模組中,比如testcase。

原則四:點選一個按鈕會開啟新的頁面,可以用return方法表示跳轉,比如return MainPage()表示跳轉到新的PO:MainPage。

原則五:只為頁面中重要的元素進行PO設計,捨棄不重要的內容。

原則六:一個動作可能產生不同結果,比如點選按鈕後,可能點選成功,也可能點選失敗,為兩種結果封裝兩個方法,click_success和click_error。

案例

案例:企業微信

以企業微信首頁為例,企業微信首頁有二個主要功能:立即註冊和企業登入。

企業微信網址:

https://

work。weixin。qq。com/

Index頁面

不懂PO 設計模式?這篇實戰文帶你搞定 PO

點選企業登入可以進入登入頁面,在頁面可以掃碼登入和企業註冊。

Login 頁面

不懂PO 設計模式?這篇實戰文帶你搞定 PO

點選企業註冊可以進入註冊頁面,在頁面可以輸入相關資訊進行註冊。

Register頁面

不懂PO 設計模式?這篇實戰文帶你搞定 PO

用page object原則為頁面建模,這裡涉及三個頁面:首頁,登入,註冊。在程式碼中建立對應的三個類Index,Login,Register:

• 登陸頁⾯提供login findPassword功能

– Login類 + login findPassword⽅法

• 登入頁⾯內的元素有多少並不關⼼,隱藏內部界⾯控制元件

• 登入成功和失敗會分別返回不同的頁⾯

– findPassword

– loginSuccess

– loginFail

• 透過⽅法返回值判斷登入是否符合預期

UML圖

不懂PO 設計模式?這篇實戰文帶你搞定 PO

程式碼目錄結構

不懂PO 設計模式?這篇實戰文帶你搞定 PO

BasePage是所有page object的父類,它為子類提供公共的方法,比如下面的BasePage提供初始化driver和退出driver,程式碼中在base_page模組的BasePage類中使用__init__初始方法進行初始化操作,包括driver的複用,driver的賦值,全域性等待的設定(隱式等待)等等:

from time import sleep

from selenium import webdriver

from selenium。webdriver。remote。webdriver import WebDriver

class BasePage:

def __init__(self, driver: WebDriver = None):

#此處對driver進行復用,如果不存在driver,就構造一個新的

if driver is None:

# Index頁面需要用,首次使用時構造新driver

self。_driver = webdriver。Chrome()

# 設定隱式等待時間

self。_driver。implicitly_wait(3)

# 訪問網頁

self。_driver。get(self。_base_url)

else:

# Login與Register等頁面需要用這個方法,避免重複構造driver

self。_driver = driver

def close(self):

sleep(20)

self。_driver。quit()

Index是企業微信首頁的page object,它存在兩個方法,進入註冊page object和進入登入page object,這裡return方法返回page object實現了頁面跳轉,比如:goto_register方法return Register,實現從首頁跳轉到註冊頁:

class Index(BasePage):

_base_url = “https://work。weixin。qq。com/”

# 進入註冊頁面

def goto_register(self):

self。_driver。find_element(By。LINK_TEXT, “立即註冊”)。click()

# 建立Register例項後,可呼叫Register中的方法

return Register(self。_driver)

# 進入登入頁面

def goto_login(self):

self。_driver。find_element(By。LINK_TEXT, “企業登入”)。click()

# 建立Login例項後,可呼叫Login中的方法

return Login(self。_driver)

Login是登入頁面的page object,主要功能有:進入註冊頁面,掃描二維碼,因此建立兩個方法代表兩個功能:scan_qrcode和goto_registry。程式碼跟上面相似,不過多介紹:

from selenium。webdriver。common。by import By

from test_selenium。page。base_page import BasePage

from test_selenium。page。register import Register

class Login(BasePage):

# 掃描二維碼

def scan_qrcode(self):

pass

# 進入註冊頁面

def goto_registry(self):

self。_driver。find_element(By。LINK_TEXT, “企業註冊”)。click()

return Register(self。_driver)

Register是註冊頁面的page object,主要功能是填寫正確註冊資訊,當填寫錯誤時,返回錯誤資訊。register方法實現了正確的表格填寫,當填寫完畢時返回自身(頁面還停留在註冊頁)。get_error_message方法實現了錯誤填寫的情況,如果填寫錯誤,就收集錯誤內容並返回:

from selenium。webdriver。common。by import By

from test_selenium。page。base_page import BasePage

class Register(BasePage):

# 填寫註冊資訊,此處只填寫了部分資訊,並沒有填寫完全

def register(self, corpname):

# 進行表格填寫

self。_driver。find_element(By。ID, “corp_name”)。send_keys(corpname)

self。_driver。find_element(By。ID, “submit_btn”)。click()

# 填寫完畢,停留在註冊頁,可繼續呼叫Register內的方法

return self

#填寫錯誤時,返回錯誤資訊

def get_error_message(self):

# 收集錯誤資訊並返回

result=[]

for element in self。_driver。find_elements(By。CSS_SELECTOR, “。js_error_msg”):

result。append(element。text)

return result

test_index模組是對上述功能的測試,它獨立於page類,在TestIndex類中只需要呼叫page類提供的方法即可,比如下面對註冊頁及登陸頁的測試使用了test_register和test_login方法:

from test_selenium。page。index import Index

class TestIndex:

# 所有步驟前的初始化

def setup(self):

self。index = Index()

# 對註冊功能的測試

def test_register(self):

# 進入index,然後進入註冊頁填寫資訊

self。index。goto_register()。register(“霍格沃茲測試學院”)

# 對login功能的測試

def test_login(self):

# 從首頁進入到註冊頁

register_page = self。index。goto_login()。goto_registry()\

。register(“測吧(北京)科技有限公司”)

# 對填寫結果進行斷言,是否填寫成功或者填寫失敗

assert “請選擇” in “|”。join(register_page。get_error_message())

# 關閉driver

def teardown(self):

self。index。close()

最後奉上為你準備的軟體測試教程資料,包括用例模板、計劃模板、報告模板、效能調優、自動化教程、測開模板、簡歷模板、面試技巧、大廠真題等,一定有你需要的!點選下方連結檢視獲取方式:

不懂PO 設計模式?這篇實戰文帶你搞定 PO

--福利福利:

一線名企大廠內推通道 >>>

史上最全軟體測試資料文件下載 >>>

如何從一個只會點滑鼠的手工測試變成測試開發 >>>

更多技術乾貨請關注霍格沃茲測試學院官方機構號:

測吧@測吧

——推薦閱讀:

測試開發是什麼?為什麼現在那麼多公司都要招聘測試開發?

軟體測試的崗位會越來越少嗎?

軟體測試真的幹到35就幹不動了嗎?