什麼是Page ObjectModel模式
Page Objects是selenium的一種測試設計模式,主要將每一個頁面看做是一個class。class的內容主要包括屬性和方法,屬性不難理解,就是這個頁面中的元素對象,好比輸入用戶名的輸入框,輸入登錄密碼的輸入框,登錄按鈕,這個頁面的url等,而方法,主要是指這個頁面能夠提供的具體功能。
爲何選擇POM?
咱們先看一段簡單的代碼以下:web
from selenium import webdriver import time driver = webdriver.Firefox() driver.implicitly_wait(30) # 啓動瀏覽器,訪問百度 driver.get("http://www.baidu.com") # 定位百度搜索框,並輸入selenium driver.find_element_by_id("kw").send_keys("selenium") # 定位百度一下按鈕並單擊進行搜索 driver.find_element_by_id("su").click() time.sleep(5) driver.quit()
這是一個簡單的小腳本。腳本維護看起來很簡單。但隨着時間測試套件的增加。隨着你在代碼中添加愈來愈多的行,事情變得艱難。
腳本維護的主要問題是,若是10個不一樣的腳本使用相同的頁面元素,而且該元素中的任何更改,則須要更改全部10個腳本。這是耗時且容易出錯的。
更好的腳本維護方法是建立一個單獨的類文件,它能夠找到Web元素,填充或驗證它們。該類能夠在使用該元素的全部腳本中重用。未來,若是web元素有變化,咱們須要在1個類文件中進行更改,而不是10個不一樣的腳本。
什麼是POM?
頁面對象模型 是 爲Web UI元素建立Object Repository的設計模式 。
在這個模型下,對於應用程序中的每一個網頁,應該有相應的頁面類。
此Page類將會找到該Web頁面的WebElements,而且還包含對這些WebElements執行操做的頁面方法。
這些方法的名稱應該按照他們正在執行的任務給出,即若是一個加載程序正在等待支付網關出現,POM方法名稱能夠是waitForPaymentScreenDisplay()。設計模式
下圖爲非POM和POM對比圖:瀏覽器
在自動化測試中,引入了Page Object Model(POM):頁面對象模式來解決,POM能讓咱們的測試代碼變得可讀性更好,高可維護性,高複用性。
POM的優點
1. POM提供了一種在UI層操做、業務流程與驗證分離的模式,這使得測試代碼變得更加清晰和高可讀性。工具
2. 對象庫與用例分離,使得咱們更好的複用對象,甚至能與不一樣的工具進行深度結合應用。組件化
3. 可複用的頁面方法代碼會變得更加優化。測試
4. 更加有效的命名方式使得咱們更加清晰的知道方法所操做的UI元素。例如咱們要回到首頁,方法名命名爲: gotoHomePage(),經過方法名便可清晰的知道具體的功能實現。優化
案例說明:
如下是簡單普通的登陸測試用例:ui
def test_login_mail(self): driver = self.driver driver.get("http://www.xxx.xxx.com") driver.find_element_by_id("idInput").clear() driver.find_element_by_id("xxxxxxx").send_keys("xxxxx") driver.find_element_by_id("xxxxxxx").clear() driver.find_element_by_id("xxxxxxx").send_keys("xxxxxx") driver.find_element_by_id("loginBtn").click()
那咱們如何進行一個改造升級呢?
改造案例思路:
第一, 咱們要分離測試對象(元素對象)和測試腳本(用例腳本),那麼咱們分別建立兩個腳本文件,分別爲: LoginPage.py 用於定義頁面元素對象,每個元素都封裝成組件(能夠看作存放頁面元素對象的倉庫) CaseLoginTest.py 測試用例腳本。
第二, 設計實現思想,一切元素和元素的操做組件化定義在Page頁面,用例腳本頁面,經過調用Page中的組件對象,進行拼湊成一個登陸腳本。url
BasePage.py:spa
#-*- coding: utf-8-*- from selenium.webdriver.support.wait importWebDriverWait from seleniumimport webdriver classAction(object): """ BasePage封裝全部頁面都公用的方法,例如driver, url ,FindElement等 """ #初始化driver、url、等 def __init__(self,selenium_driver, base_url, pagetitle): self.base_url = base_url self.pagetitle = pagetitle self.driver = selenium_driver #打開頁面,校驗頁面連接是否加載正確 def _open(self,url, pagetitle): #使用get打開訪問連接地址 self.driver.get(url) self.driver.maximize_window() #使用assert進行校驗,打開的連接地址是否與配置的地址一致。調用on_page()方法 assertself.on_page(pagetitle), u"打開開頁面失敗 %s"% url #重寫元素定位方法 def find_element(self,*loc): #returnself.driver.find_element(*loc) try: WebDriverWait(self.driver,10).until(lambdadriver: driver.find_element(*loc).is_displayed()) return self.driver.find_element(*loc) except: print u"%s 頁面中未能找到 %s 元素"%(self, loc) #重寫switch_frame方法 def switch_frame(self, loc): return self.driver.switch_to_frame(loc) #定義open方法,調用_open()進行打開連接 def open(self): self._open(self.base_url, self.pagetitle) #使用current_url獲取當前窗口Url地址,進行與配置地址做比較,返回比較結果(True False) def on_page(self,pagetitle): return pagetitlein self.driver.title #定義script方法,用於執行js腳本,範圍執行結果 def script(self,src): self.driver.execute_script(src) #重寫定義send_keys方法 def send_keys(self, loc, vaule, clear_first=True, click_first=True): try: loc = getattr(self,"_%s"% loc) if click_first: self.find_element(*loc).click() if clear_first: self.find_element(*loc).clear() self.find_element(*loc).send_keys(vaule) exceptAttributeError: print u"%s 頁面中未能找到 %s 元素"%(self, loc)
LoginPage.py:
#-*- coding: utf-8-*- from selenium.webdriver.common.by importBy import BasePage #繼承BasePage類 class LoginPage(BasePage.Action): #定位器,經過元素屬性定位元素對象 username_loc=(By.ID,"idInput") password_loc =(By.ID,"pwdInput") submit_loc =(By.ID,"loginBtn") span_loc=(By.CSS_SELECTOR,"div.error-tt>p") dynpw_loc =(By.ID,"lbDynPw") userid_loc =(By.ID,"spnUid") #Action def open(self): #調用page中的_open打開鏈接 self._open(self.base_url,self.pagetitle) #調用send_keys對象,輸入用戶名 def input_username(self, username): self.find_element(*self.username_loc).send_keys(username) #調用send_keys對象,輸入密碼 def input_password(self, password): self.find_element(*self.password_loc).send_keys(password) #調用send_keys對象,點擊登陸 def click_submit(self): self.find_element(*self.submit_loc).click() #用戶名或密碼不合理是Tip框內容展現 def show_span(self): returnself.find_element(*self.span_loc).text #切換登陸模式爲動態密碼登陸(IE下有效) def swich_DynPw(self): self.find_element(*self.dynpw_loc).click() #登陸成功頁面中的用戶ID查找 def show_userid(self): returnself.find_element(*self.userid_loc).text
Caselongintest.py
#-*- coding: utf-8-*- import sys reload(sys) sys.setdef aultencoding('utf-8') import unittest from POimportLoginPage from seleniumimport webdriver classCaselogin126mail(unittest.TestCase): """ 登陸case """ @classmethod def setUpClass(cls): cls.driver = webdriver.Chrome() cls.driver.implicitly_wait(30) cls.url ="http://xxxx.xxx.com" cls.username ="xxxxx" cls.password ="xxxxx" #用例執行體 def test_login_mail(self): #聲明LoginPage類對象 login_page=LoginPage.LoginPage(self.driver, self.url, u」xxxxx」) #調用打開頁面組件 login_page.open() #調用用戶名輸入組件 login_page.input_username(self.username) #調用密碼輸入組件 login_page.input_password(self.password) #調用點擊登陸按鈕組件 login_page.click_submit() @classmethod def tearDownClass(cls): cls.driver.quit() if __name__=="__main__": unittest.main()
使用POM進行從新構造代碼結構後,發現代碼測試用例代碼的可讀性提升不少,元素寫成組件的方式,不須要每次都寫findElement直接在腳本中調用組件就可使用。
在CaseLoginTest腳本用例執行體中,一旦咱們輸入 login_page並敲入一個點時,LoginPage頁面中的元素對象組件都顯示出來。而且定義好的PageObject組件能夠重複在其它的腳本中進行使用,減小了代碼的工做量,也方便對腳本進行後期的維護管理,當元素屬性發生變化時,咱們只須要對一個PageObaject頁面中的對象組件定義進行更改便可。
最後作個總結,全部代碼請手動輸入,不要直接拷貝。
再次對POM進行小結:
1. POM是selenium webdriver自動化測試實踐對象庫設計模式2. POM使得測試腳本更易於維護3. POM經過對象庫方式進一步優化了元素、用例、數據的維護組織