研究Selenium + python 自動化測試有近兩個月了,不能說非常熟練,起碼對selenium自動化的執行有了深入的認識。
從最初無結構的代碼,到類的使用,方法封裝,從原始函數調用,到重定義函數。從變量驅動,到數據驅動,再到關鍵字驅動,一步一步的默默走向自動化框架的構建。
雖然還有沒有投入使用,只是寫幾個demo,就慢慢的發現了 selenium自動用例腳本,相似功能地方,代碼基本都是一樣的,界面元素換個查找方式,把原來的使用 xpath方式,改為使用 id 查找,需要對每個用例腳本都要改,雖然幾個用例看不出什么工作量,但是重復findElement的代碼,已經讓我們感到了代碼的笨重。如果某些定位發生了改變,我們就得貫穿整個測試代碼進行調整元素定位,這樣就會導致我們的腳本在后期,難以維護。
因此通過Page Object Model 我們可以創建更加健壯代碼,並減少或者消除重復的測試代碼,從而也能夠提高代碼的可讀性,減少編寫腳本的工作量。Page Object Model的實現,就是通過分離測試對象和測試腳本的抽象來實現的。
PO項目實戰演示Demo源碼:(源碼會隨着結構調整不斷更新,但是整體結構不會變動)
Page Object Model 簡稱POM
下面通過一個實例演示POM的實現
在此之前,先看一個段比較普通的測試用例代碼片段:
-
1 .... 2 #測試用例 3 def test_login_mail(self): 4 driver = self.driver 5 driver.get("http://mail.126.com") 6 driver.find_element_by_id("idInput").clear() 7 driver.find_element_by_id("idInput").send_keys("auto_tester") 8 driver.find_element_by_id("pwdInput").clear() 9 driver.find_element_by_id("pwdInput").send_keys("123456") 10 driver.find_element_by_id("loginBtn").click() 11 ....
這段腳本實現,是126郵箱登錄功能的一個測試用例,單純從這段代碼來看,並不能直觀知道他實現了什么業務,而且對於同一個元素,都出現重復的定位代碼,只一個簡單的登錄功能,一個用例只操作3個元素,就暴露出來了一些代碼冗余和可讀性的問題。那么如果有更多功能和更多用例,那么用例代碼更加厚重,可讀性也是大大降低,我們需要想辦法對此代碼進行優化,通過POM實現登錄功能。
首先,我們要分離測試對象(元素對象)和測試腳本(用例腳本),那么我們分別創建兩個腳本文件, LoginPage.py 用於定義頁面元素對象,每一個元素都封裝成組件(可以看做存放頁面元素對象的倉庫) CaseLoginTest.py 測試用例腳本。我們的實現思想,一切元素和元素的操作組件化定義在Page頁面,用例腳本頁面,通過調用Page中的組件對象,進行拼湊成一個登錄腳本。
在寫這兩個腳本之前,我先對WebDriver中的一些方法進行重定義,以方便我們在寫PO的時候,更簡潔,快速。
BasePage.py:
-
1 #-*- coding: utf-8 -*- 2 __author__ ='tsbc' 3 from selenium.webdriver.support.wait importWebDriverWait 4 from selenium import webdriver 5 classAction(object): 6 """ 7 BasePage封裝所有頁面都公用的方法,例如driver, url ,FindElement等 8 """ 9 #初始化driver、url、等 10 def __init__(self, selenium_driver, base_url, pagetitle): 11 self.base_url = base_url 12 self.pagetitle = pagetitle 13 self.driver = selenium_driver 14 15 #打開頁面,校驗頁面鏈接是否加載正確 16 def _open(self, url, pagetitle): 17 #使用get打開訪問鏈接地址 18 self.driver.get(url) 19 self.driver.maximize_window() 20 #使用assert進行校驗,打開的鏈接地址是否與配置的地址一致。調用on_page()方法 21 assert self.on_page(pagetitle), u"打開開頁面失敗 %s"% url 22 23 #重寫元素定位方法 24 def find_element(self,*loc): 25 #return self.driver.find_element(*loc) 26 try: 27 WebDriverWait(self.driver,10).until(lambda driver: driver.find_element(*loc).is_displayed()) 28 return self.driver.find_element(*loc) 29 except: 30 print u"%s 頁面中未能找到 %s 元素"%(self, loc) 31 32 #重寫switch_frame方法 33 def switch_frame(self, loc): 34 return self.driver.switch_to_frame(loc) 35 #定義open方法,調用_open()進行打開鏈接 36 37 def open(self): 38 self._open(self.base_url, self.pagetitle) 39 40 #使用current_url獲取當前窗口Url地址,進行與配置地址作比較,返回比較結果(True False) 41 def on_page(self, pagetitle): 42 return pagetitle in self.driver.title 43 44 #定義script方法,用於執行js腳本,范圍執行結果 45 def script(self, src): 46 self.driver.execute_script(src) 47 48 #重寫定義send_keys方法 49 def send_keys(self, loc, vaule, clear_first=True, click_first=True): 50 try: 51 loc = getattr(self,"_%s"% loc) 52 if click_first: 53 self.find_element(*loc).click() 54 if clear_first: 55 self.find_element(*loc).clear() 56 self.find_element(*loc).send_keys(vaule) 57 exceptAttributeError: 58 print u"%s 頁面中未能找到 %s 元素"%(self, loc)
LoginPage.py:
-
1 #-*- coding: utf-8 -*- 2 __author__ ='tsbc' 3 4 from selenium.webdriver.common.by importBy 5 importBasePage 6 7 #繼承BasePage類 8 classLoginPage(BasePage.Action): 9 10 #定位器,通過元素屬性定位元素對象 11 username_loc =(By.ID,"idInput") 12 password_loc =(By.ID,"pwdInput") 13 submit_loc =(By.ID,"loginBtn") 14 span_loc =(By.CSS_SELECTOR,"div.error-tt>p") 15 dynpw_loc =(By.ID,"lbDynPw") 16 userid_loc =(By.ID,"spnUid") 17 18 #Action 19 def open(self): 20 #調用page中的_open打開連接 21 self._open(self.base_url, self.pagetitle) 22 #調用send_keys對象,輸入用戶名 23 def input_username(self, username): 24 self.find_element(*self.username_loc).send_keys(username) 25 #調用send_keys對象,輸入密碼 26 def input_password(self, password): 27 self.find_element(*self.password_loc).send_keys(password) 28 #調用send_keys對象,點擊登錄 29 def click_submit(self): 30 self.find_element(*self.submit_loc).click() 31 #用戶名或密碼不合理是Tip框內容展示 32 def show_span(self): 33 return self.find_element(*self.span_loc).text 34 #切換登錄模式為動態密碼登錄(IE下有效) 35 def swich_DynPw(self): 36 self.find_element(*self.dynpw_loc).click() 37 #登錄成功頁面中的用戶ID查找 38 def show_userid(self): 39 return self.find_element(*self.userid_loc).text 40 41 CaseLoginTest.py: 42 #-*- coding: utf-8 -*- 43 __author__ ='tsbc' 44 import sys 45 reload(sys) 46 sys.setdefaultencoding('utf-8') 47 import unittest 48 from PO importLoginPage 49 from selenium import webdriver 50 51 classCaselogin126mail(unittest.TestCase): 52 """ 53 登錄126郵箱的case 54 """ 55 @classmethod 56 def setUpClass(cls): 57 cls.driver = webdriver.Chrome() 58 cls.driver.implicitly_wait(30) 59 60 cls.url ="http://mail.126.com" 61 cls.username ="auto_tester" 62 cls.password ="123456" 63 64 #用例執行體 65 def test_login_mail(self): 66 #聲明LoginPage類對象 67 login_page =LoginPage.LoginPage(self.driver, self.url, u"網易") 68 69 #調用打開頁面組件 70 login_page.open() 71 #調用用戶名輸入組件 72 login_page.input_username(self.username) 73 #調用密碼輸入組件 74 login_page.input_password(self.password) 75 #調用點擊登錄按鈕組件 76 login_page.click_submit() 77 @classmethod 78 def tearDownClass(cls): 79 cls.driver.quit() 80 81 if __name__ =="__main__": 82 unittest.main()
通過使用POM進行重新構造代碼結構后,發現代碼測試用例代碼的可讀性提高很多,元素寫成組件的方式,不需要每次都寫findElement直接在腳本中調用組件就可以使用。在CaseLoginTest腳本用例執行體中,一旦我們輸入 login_page並敲入一個點時,LoginPage頁面中的元素對象組件都顯示出來。並且定義好的PageObject組件可以重復在其它的腳本中進行使用,減少了代碼的工作量,也方便對腳本進行后期的維護管理,當元素屬性發生變化時,我們只需要對一個PageObaject頁面中的對象組件定義進行更改即可。
<簡單對Page Object Model使用實例進行描述,希望能夠對Selenium自動化的POM理解有所幫助>