Page Object設計模式
Page Object 設計思想
Page Object 就是在測試中將操作細節和驗證分開的
設計原則 一
只為頁面中重要的元素建立page類
設計原則 二
如果頁面A 導航到頁面B, Page A 應當return Page B
Pg object
PO 六大原則
1、一個 public 方法代表一個公共的服務。就是說一個方法代替頁面上的某個操作
2、PageObject 中的方法細節不可暴露在外,通過提供公共服務介面的形式提供給外部
3、一般不需要在 PageObject 中斷言
4、當有頁面跳轉的操作時候,執行這個方法時應該在方法結束返回時能夠跳轉到另一個頁面中
5、我們只需要對頁面中我們需要的重要的內容進行封裝
6、頁面中相同的元件,但是不同的操作應該要被拆成不同的方法進行封裝
面試提問
1、Page Object 設計思想解決的是什麼樣的問題
1、程式碼冗餘明顯降低:二次封裝Selenium方法和提取公共方法,提高程式碼複用性
2、程式碼的閱讀性明顯提升:因為三層分級,將不同內容進行不同的封裝,整體程式碼閱讀性提升
3、程式碼維護性明顯提升:UI測試中,頁面若經常變動,程式碼的維護量隨之增多;因為三層分級,我們只需要修改頁面物件的程式碼,如元素物件或者操作物件的方法,不用修改測試用例的程式碼,也不影響測試用例的正常執行
4、降低程式碼耦合性
2、Page Object 編寫的思路
一個新增成員的步驟 1、登入_login頁面 2、登入之後進入首頁_main頁面 3、點選新增成員_main頁面 4、填寫新增資訊_add_member頁面 5、點選儲存_add_member頁面 6、返回通訊錄_add_membe頁面 7、加斷言做驗證_contact頁面
實戰時序圖建立
專案開始前,建立自己的一個時序圖,便於思考
@startuml
autonumber
participant 企業微信主頁面 as main
participant 通訊錄頁面 as contact
participant 新增成員頁面 as add_member
main -> contact: 點選通訊錄
main -> add_member: 點選新增成員
contact -> add_member: 點選新增成員
add_member -> contact: 填寫成員資料, 點選儲存
contact -> contact: 獲取成員列表斷言
@enduml
黃色長方形代表所在頁面, 線代表在這個頁面的操作,箭頭代表跳轉到哪個頁面
實戰練習
PO 構建圖
1、返回值需要設定到箭頭指向的地方
專案結構
注意:
在vscode中編輯,需要匯入包的時候所有包結構下都要加入__init__.py
PO 資料夾為 構造PO模型
Po/base_function.py 基礎的公用函式類,比如瀏覽器複用初始化
"""
公共函式類的繼承
"""
import time
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions
from selenium.webdriver.common.by import By
from selenium.webdriver import ActionChains
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.keys import Keys
from selenium.webdriver import TouchActions
class PoBase:
# 新增baseurl, 可以支援測試用例的靈活配置起始頁
# basepage 完全和專案解耦
_base_url = ""
def __init__(self, base_driver=None):
# base_driver, 解決連結不斷初始化的過程
if base_driver is None:
self.chrome_options = Options()
self.chrome_options.add_experimental_option("debuggerAddress", "127.0.0.1:9222") # 指定配置好的 chrom
self.chrome_options.add_experimental_option("w3c", False)
self.chrome_driver = r"D:\my_project\git_deve\development\python_work\test_selenium\chromedriver.exe" # 驅動路徑
self.driver = webdriver.Chrome(self.chrome_driver, chrome_options=self.chrome_options) # 加入驅動設定
self.driver.get(self._base_url) # 發起請求
# self.driver.maximize_window() # 設定為最大化
self.driver.implicitly_wait(5) # 新增一個隱式等待預設等待3秒
else:
self.driver = base_driver
# 封裝driver,以便於新增Logger
def find_xpath(self, locator):
return self.driver.find_element_by_xpath(locator)
def finds_xpath(self, locator):
return self.driver.find_elements_by_xpath(locator)
testcase/test_add_member.py 為測試用例得一個編寫檔案是入口檔案
"""
根據業務需要新增斷言
通過鏈式呼叫的方式更加方便的描述業務邏輯
"""
from page_object_base_demo.po.main_page import MainPage
import pytest
class TestAddMember:
@pytest.mark.parametrize("name, id, phone", [('od', '123', '13111111111')]) # 引數化資料驅動
def test_add_member(self, name, id, phone):
main_page = MainPage()
# 1.跳轉到add_member頁面
# 2.做一個新增成員操作,然後點選儲存 跳轉到通訊錄頁面
# 3。通訊錄頁面獲取成員資訊。作為斷言
assert "od" in main_page.goto_contact().goto_add_member().add_member(name, id, phone).get_member_list()
@ pytest.mark.parametrize("name, id, phone", [('keke', '1235', '13111111112')])
def test_index_member(self, name, id, phone):
main_page = MainPage()
# 1.直接從首頁進入到新增成員
# 2.做一個新增成員操作,然後點選儲存 跳轉到通訊錄頁面
# 3。通訊錄頁面獲取成員資訊。作為斷言
assert "keke" in main_page.goto_add_member().add_member(name, id, phone).get_member_list()
PO / main_page.py 根據上面的時序圖編寫,為入口檔案, 裡面的方法即為時序圖下的兩個箭頭過程
兩個函式返回的值為箭頭指向的,另一個類裡面的過程。
"""
編碼第一步,構造PO模型,實現設定為空
構造頁面相關類和方法
黃色的方塊代表一個類
每條線代表這個頁面提供的操作
箭頭的始端為開始頁面
箭頭的末端為跳轉頁面, 即對應方法需要返回跳轉頁面的例項物件
實現暫時實際為空
這是個主頁面
"""
import time
from page_object_base_demo.po.base_function import PoBase
class MainPage(PoBase):
_base_url = "https://work.weixin.qq.com/wework_admin/frame#index"
contact = "//span[@class='frame_nav_item_title'][contains(text(),'通訊錄')]"
add_index = '//*[@id="_hmt_click"]/div[1]/div[4]/div[2]/a[1]/div/span[2]'
def goto_contact(self):
'''
跳轉到通訊頁
:return:ContactPage
'''
from page_object_base_demo.po.contact_page import ContactPage
self.find_xpath(self.contact).click()
return ContactPage(self.driver)
def goto_add_member(self):
'''
跳轉到新增成員頁
:return:
'''
from page_object_base_demo.po.add_member_page import AddMemberPage
self.find_xpath(self.add_index).click()
return AddMemberPage(self.driver)
PO / add_member_page.py 根據上面的時序圖編寫,為新增使用者的兩個過程辦法:
'''
新增成員頁面
'''
# from page_object_base_demo.po.contact_page import ContactPage
from page_object_base_demo.po.base_function import PoBase
class AddMemberPage(PoBase):
user_name = '//*[@id="username"]'
user_acctid = '//*[@id="memberAdd_acctid"]'
user_phone = '//*[@id="memberAdd_phone"]'
user_save = "//div[contains(@class,'member_edit')]//div[1]//a[2]"
def add_member(self, name, id, phone):
'''
新增成員操作
:return:
'''
from page_object_base_demo.po.contact_page import ContactPage
print('新增成員')
self.find_xpath(self.user_name).send_keys(name)
self.find_xpath(self.user_acctid).send_keys(id)
self.find_xpath(self.user_phone).send_keys(phone)
self.find_xpath(self.user_save).click()
return ContactPage(self.driver)
PO / contact_page.py 根據上面的時序圖編寫,跳轉到通訊錄頁面的方法
'''
通訊錄頁面
'''
import time
from page_object_base_demo.po.base_function import PoBase
class ContactPage(PoBase):
add_people = "//div[contains(@class,'js_operationBar_footer ww_operationBar')]//a[contains(@class,'qui_btn ww_btn js_add_member')][contains(text(),'新增成員')]"
t_body = '//*[@id="member_list"]/tr'
def goto_add_member(self):
'''
點選跳轉新增成員頁
:return:
'''
time.sleep(1)
from page_object_base_demo.po.add_member_page import AddMemberPage
print('準備開始新增成員。。。')
self.find_xpath(self.add_people).click()
return AddMemberPage(self.driver)
def get_member_list(self):
'''
獲取成員列表
:return: list
'''
time.sleep(1)
print('獲取成員列表')
t_body = self.finds_xpath(self.t_body)
name_list = []
for i in t_body:
tdList = i.find_elements_by_tag_name("td")
name_list.append(tdList[1].text)
print(name_list)
return name_list
完。