為UI頁面寫測試用例時(比如web頁面,移動端頁面),測試用例會存在大量元素和操作細節。當UI變化時,測試用例也要跟著變化, PageObject 很好的解決了這個問題!
使用UI自動化測試工具時(包括selenium,appium等),如果無統一模式進行規範,隨著用例的增多會變得難以維護,而 PageObject 讓自動化指令碼井井有序,將 page 單獨維護並封裝細節,可以使 testcase 更穩健,不需要大改大動。
使用
具體做法:把元素資訊和操作細節封裝到Page類中,在測試用例上呼叫Page物件(PageObject),比如存在一個功能“選取相簿標題”,需要為之建立函式selectAblumWithTitle(),函式內部是操作細節findElementsWithClass(‘album’)等:
以選“取相簿標題”舉例,虛擬碼如下:
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的目的是透過給頁面建模,從而對應用程式的使用者變得有意義:
如果你想導航到另一個頁面,初始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頁面
點選企業登入可以進入登入頁面,在頁面可以掃碼登入和企業註冊。
Login 頁面
點選企業註冊可以進入註冊頁面,在頁面可以輸入相關資訊進行註冊。
Register頁面
用page object原則為頁面建模,這裡涉及三個頁面:首頁,登入,註冊。在程式碼中建立對應的三個類Index,Login,Register:
• 登陸頁⾯提供login findPassword功能
– Login類 + login findPassword⽅法
• 登入頁⾯內的元素有多少並不關⼼,隱藏內部界⾯控制元件
• 登入成功和失敗會分別返回不同的頁⾯
– findPassword
– loginSuccess
– loginFail
• 透過⽅法返回值判斷登入是否符合預期
UML圖
程式碼目錄結構
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()
最後奉上為你準備的軟體測試教程資料,包括用例模板、計劃模板、報告模板、效能調優、自動化教程、測開模板、簡歷模板、面試技巧、大廠真題等,一定有你需要的!點選下方連結檢視獲取方式:
--福利福利:
一線名企大廠內推通道 >>>
史上最全軟體測試資料文件下載 >>>
如何從一個只會點滑鼠的手工測試變成測試開發 >>>
更多技術乾貨請關注霍格沃茲測試學院官方機構號:
測吧@測吧
——推薦閱讀:
測試開發是什麼?為什麼現在那麼多公司都要招聘測試開發?
軟體測試的崗位會越來越少嗎?
軟體測試真的幹到35就幹不動了嗎?