1、PO模式的思想
- 原理:
- 將頁面的元素定位和元素行為封裝成一個page類
- 類的屬性:元素的定位
- 類的行為:元素的操作
- 頁面對象和測試用例分離
- 測試用例:
- 調用所需要頁面對象中的行為,組成測試用例
- 測試用例中,只需要含有頁面函數的調用和斷言,不應該出現元素定位等其他的操作,如果寫測試用例中出現需要其他的額外的操作,可以想辦法封裝到頁面對象或者元素定位中
- 好處:
- 當某個頁面的元素發生變化,只需要修改該頁面對象中的代碼即可,測試用例不需要修改
- 提高代碼的重用率,結構清晰,維護代碼容易
- 測試用例發生變化是,不需要或者只需要修改少數頁面對象代碼即可
2、使用unittest框架實現PO模式
- 新建一個包,命名為PageObjects,包中用來封裝各個頁面的功能
- 頁面對象封裝的類中,函數用來實現頁面的功能,在類的初始化函數中,使用傳參(driver)的方式完成,頁面只需要實現對應功能即可,具體傳入什么樣的測試,在測試用例的前置條件中實現,可以提高函數的重用率
-
頁面對象封裝函數
1 from selenium.webdriver.support import expected_conditions as EC 2 from selenium.webdriver.support.wait import WebDriverWait 3 from selenium.webdriver.common.by import By 4 class LoginPage: 5 def __init__(self,driver): 6 self.driver=driver 7 8 def login(self,username,pwd,remember_user=True): 9 # 等待定位元素出現 10 WebDriverWait(self.driver,10).until(EC.visibility_of_element_located((By.XPATH,"//input[@name='phone']"))) 11 # 輸入用戶名 12 self.driver.find_element_by_xpath("//input[@name='phone']").send_keys(username) 13 # 輸入密碼 14 self.driver.find_element_by_xpath("//input[@name='password']").send_keys(pwd) 15 # 定位記住手機號元素 16 remember_ele=self.driver.find_element_by_xpath("//input[@name='remember_me']") 17 # 判斷時候記住手機號 18 if remember_user==True: 19 remember_ele.checked=True 20 else: 21 remember_ele.checked=False 22 # 點擊登錄 23 self.driver.find_element_by_xpath("//button[text()='登錄']").click()
-
測試用例調用頁面對象函數,代碼如下
1 def test_login_success(self): 2 # 2、步驟 3 # 實例化LoginPage類 4 lg=LoginPage(self.driver) 5 # 調用login方法 6 lg.login("登錄賬號","登錄密碼") 7 # 3、斷言 8 self.assertTrue(IndexPage(self.driver).isExist_logout_ele())
- Index_page頁面封裝函數代碼
from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.support.wait import WebDriverWait from selenium.webdriver.common.by import By class IndexPage: def __init__(self,driver): self.driver=driver def isExist_logout_ele(self): try: WebDriverWait(self.driver,10).until(EC.visibility_of_element_located((By.XPATH,"//a[text()='退出']"))) return True except: return False
- 測試用例的前置條件代碼
import unittest from selenium import webdriver from PageObjects.login_page import LoginPage from PageObjects.index_page import IndexPage class TestLogin(unittest.TestCase): def setUp(self): # 1、前置條件 url="http://ip:host/Index/login.html" self.driver=webdriver.Chrome() self.driver.maximize_window() self.driver.get(url)
- 運行測試用例可以在測試用例最后面使用一下代碼,然后運行該代碼
if __name__ == '__main__': unittest.main()
- 運行結果如下,表示運行成功
-
- 頁面對象封裝的類中,函數用來實現頁面的功能,在類的初始化函數中,使用傳參(driver)的方式完成,頁面只需要實現對應功能即可,具體傳入什么樣的測試,在測試用例的前置條件中實現,可以提高函數的重用率
- 新建一個包,命名為TestCases,包中用來寫測試用例,測試用例中用到那個頁面對象中的函數時,只需要調用頁面對象中的函數就可以了
- 新建一個包,命名為TestDatas,用來寫測試數據,將測試數據的類導入到測試用例模塊中,進行使用,這樣的話,測試數據便於管理,測試數據添加、修改、刪除不用修改測試用例
- 在TestCases下面新建一個公用的數據管理.py文件Common_Datas.py和模塊數據的.py文件login_datas.py,如下t圖:
#Common_Datas.py web_login_url="http://ip:port/Index/login.html" #login_datas.py #正常場景---測試數據 success_data={"user":"18000123456","pwd":"123456"} # 異常用例--手機號格式不正確(大於11位,小於11位,為空,不在號碼段) error_data=[ {"user": "180001234561", "pwd": "123456","check":"請輸入正確的手機號"}, {"user": "1800012345", "pwd": "123456","check":"請輸入正確的手機號"}, {"user": "10684723485", "pwd": "123456","check":"請輸入正確的手機號"}, {"user": "", "pwd": "123456","check":"請輸入手機號"}, {"user": "18000123456", "pwd": "","check":"請輸入密碼"}, {"user": "", "pwd": "","check":"請輸入手機號"}, ]
- 在測試用例模塊使用數據管理模塊中的數據
1 import unittest 2 from selenium import webdriver 3 from PageObjects.login_page import LoginPage 4 from PageObjects.index_page import IndexPage 5 from TestDatas import Common_Datas as CD 6 from TestDatas import login_datas as ld 7 from ddt import ddt,data 8 9 @ddt 10 class TestLogin(unittest.TestCase): 11 @classmethod 12 def setUpClass(cls): 13 # 1、前置條件 14 cls.driver=webdriver.Chrome() 15 cls.driver.maximize_window() 16 cls.driver.get(CD.web_login_url) 17 # 實例化LoginPage類 18 cls.lg=LoginPage(cls.driver) 19 @classmethod 20 def tearDownClass(cls): 21 cls.driver.quit() 22 23 def tearDown(self): 24 self.driver.refresh() 25 pass 26 # 正常用例--登錄成功 27 def test_login_1_success(self): 28 # 2、步驟 29 30 # 調用login方法 31 self.lg.login(ld.success_data['user'],ld.success_data['pwd']) 32 # 3、斷言 33 self.assertTrue(IndexPage(self.driver).isExist_logout_ele()) 34 # 異常用例--手機號碼格式不正確(大於11位,小於11位,為空,不在號碼段) ddt 35 @data(*ld.error_data) 36 def test_login_0_user_wrongFormat(self,item): 37 self.lg.login(item['user'],item['pwd']) 38 self.assertEqual(self.lg.get_errorMsg_form_loginArea(),item['check'])
備注:
setUpClass(cls)函數和tearDownClass(cls)只在整個用例開始和結束時運行一次,必須使用@classmethod來修飾,如上面的異常測試用例有6條,在用例開始前和結束時只運行一次
setUp(self)函數和tearDown(self)在每個測試用例開始和結束時運行,如果使用這兩個函數,上面的6條用例,每運行一個,就會運行一次這兩個函數,總共運行6次
tearDown(self)可以和setUpClass(cls)函數和tearDownClass(cls)函數結合使用,如上面的界面刷新self.driver.refresh()和頁面關閉cls.driver.quit()
- 測試用例類型相同的測試數據可以設計在一起,使用ddt在測試用例運行時分別執行測試用例
- 使用unittest框架,運行測試用例,如下:
1 import unittest 2 from TestCases.test_login import TestLogin 3 from HTMLTestRunnerCN import HTMLTestReportCN 4 # 創建一個容器,用來存測試用例 5 suite=unittest.TestSuite() 6 # 加載測試用例的類的實例 7 loader=unittest.TestLoader() 8 # 將測試用例加載到suite容器中 9 suite.addTest(loader.loadTestsFromTestCase(TestLogin)) 10 # 打開文件,用來寫測試報告 11 with open("test_result.html",'wb') as file: 12 13 # runner=unittest.TextTestRunner(verbosity=1) 14 15 runner=HTMLTestReportCN( stream=file, verbosity=2, title="web自動化測試", description="第一個web自動化測試", tester="wsk") 16 # 運行測試用例 17 runner.run(suite)
- 運行結果,如下圖:
在寫測試用例時,如果使用unittest框架可以使用函數名來控制代碼的執行順序,如:
ok test_login_0_user_wrongFormat_6 (TestCases.test_login.TestLogin) -------------------- 0
ok test_login_1_success (TestCases.test_login.TestLogin)------------------------------------------1 前面都一樣,0比1小,先運行
- 在TestCases下面新建一個公用的數據管理.py文件Common_Datas.py和模塊數據的.py文件login_datas.py,如下t圖: