Page Object Model:
PO設計模式是selenium自動化測試中最佳的設計模式之一,主要體現在對界面交互細節的封裝,也就是在實際測試中只關注業務流程就OK了
傳統的設計中,在新增測試用例之后,代碼會有以下幾個問題:
1.易讀性差:一連串的find element會使代碼顯得雜亂無章
2.可擴展性不好:用例孤立,無法擴展
3.可復用性差:無公共方法,很難復用
4.可維護性差:一旦元素變化,需要維護修改大量測試用例
因此考慮到優化:
PO模式是一種自動化測試設計模式,講頁面定位和業務操作分開,也就是把對象的定位和測試腳本分開,從而提供可維護性。
首先抽象封裝一個BasePage類,這個基類擁有一些指向Webdriver實例的屬性,然后每一個Page繼承基類BasePage,可以通過driver管理每一個Page中的元素,而且在Page中將這些操作封裝為一個一個的方法。TestCase繼承unittest里面的TestCase類,並且依賴page類,進行組織測試步驟的工作。
這樣做的好處,就是有元素變化,只需要維護每一個Page就行了,測試步驟變化,只需要維護TestCase即可
PO各個核心要素的介紹:
BasePage:
class BasePage(object): def __init__(self,driver): self.driver = driver pass
Page:
from SeleniumProject.PO.BasePage import BasePage class LoginBase(BasePage): # 定位元素,括號中是通過find_element來獲取元素的屬性 uname = () pwd = () def set_uname(self,uname): name =self.driver.find_element(*LoginBase.uname) name.send_keys("用戶名") def set_pwd(self,pwd): password = self.driver.find_element(*LoginBase.pwd) password.send_keys("密碼") pass
TestCase:
from unittest import TestCase import unittest from selenium import webdriver class Test_Login(TestCase): def setUp(self): self.driver = webdriver.Chrome() self.driver.get("https://cn.bing.com/") # 測試步驟 def test_Login(self): self.driver.get(self.base_url) pass def tearDown(self): self.driver.quit() if __name__ == "__main__": unittest.main()
下面舉一個簡單的例子來看一下PO模式:
業務要求就是,用Chrome瀏覽器,在https://cn.bing.com/中搜索“墨菲特”,然后點擊搜索按鈕,再關閉瀏覽器
基類BasePage類:
from selenium import webdriver from selenium.webdriver.support.wait import WebDriverWait from selenium.webdriver.support import expected_conditions as EC class BasePage(object): """BasePage封裝所有頁面的公有方法,例如url、driver、find_element""" # 構造函數里面的參數就是類的所有參數 def __init__(self,selenuime_driver,base_url): self.driver = selenuime_driver self.url = base_url # 定義一個私有方法,其他類不能調用該方法 def _open(self,url): self.driver.get(url) self.driver.maximize_window() # 定義open()方法,調用_open()方法 def open(self): self._open(self.base_url) # 重寫find_element()方法,參數為任意數量的(帶*的參數) # 此方法是為了保證元素是可見的 def find_emelemt(self,*loc): try: # 保證元素可見 WebDriverWait(self.driver,10).until(EC.visibility_of_all_elements_located(loc)) return self.driver.find_element(*loc) except: print("頁面中沒有%s %" % (self.loc)) # 定義script()方法,用於執行JS腳本,比方上上傳文件啥的 def script(self,src): self.driver.excute_script(src) # 定義頁面跳轉方法,比方說有的頁面有frame嵌套 def switch_frame(self,loc): return self.driver.switch_to_frame(loc) # 重新定義send_keys()方法,為了保證搜索按鈕是否存在,還有有的輸入框中默認有值,要清空 def send_keys(self,loc,value,clear_first=True,click_first=True): try: # getattr方法相當於實現了self.loc loc = getattr(self,"_%s"%loc) # 是否存在搜索按鈕 if click_first: self.find_element(*loc).click() # 清空搜索框中的值,並輸入需要搜索的值 if clear_first: self.find_emelemt(*loc).clear() self.find_emelemt(*loc).send_keys(value) except: print("頁面上未找到%s元素"%(self.loc))
Page類:
from selenium.webdriver.common.by import By from SeleniumProject.PO.BasePage import BasePage class SearchPage(BasePage): # 定位元素 search_loc = (By.NAME,"q") #搜索框 btn_loc = (By.NAME,"go") #搜索按鈕 # 重寫父類的open()方法 def open(self): self._open(self.base_url) def search_content(self,content): # 調用父類的find_emelemt,然后將本類的參數傳入 content1 = self.find_emelemt(*self.search_loc) content1.send_keys(content) def btn_click(self): btn1 = self.find_emelemt(*self.btn_loc) btn1.click()
TestCase類:
from unittest import TestCase import unittest from selenium import webdriver from time import sleep from SeleniumProject.PO.Search import SearchPage class CaseRun(TestCase): def setUp(self): self.driver = webdriver.Chrome() sleep(3) self.url = "https://cn.bing.com/" sleep(3) self.content = "墨菲特" # 測試步驟 def test_search(self): bing_page = SearchPage(self.driver,self.url) bing_page.open() sleep(3) bing_page.search_content(self.content) sleep(3) bing_page.btn_click() sleep(3) def tearDown(self): self.driver.quit() if __name__ == "__main__": unittest.main()