1. 程式人生 > 其它 >Page Object設計模式

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頁面

實戰時序圖建立

專案開始前,建立自己的一個時序圖,便於思考

https://plantuml.ceshiren.com/uml/IomjoSyhpKrABU8gI2mgoKpEB4ZCAr5uigVnoOvPJ_UjUx9_uPCTkryktFfoxYA5n6A5tCJCF2u55BKl6sQzMBV-wTwf25N9-NabYSabQ0gVRT_zj6l1iuu9JoVEGAXDJ4c9pqq5kq212sIyWguTp00hXUTDEv-sxuPRWQG4eG-c2jbye4Ae6KIfHIX0iVtfmjLFssP2LBtOsl9isiptUtgW9ZpPF_VfMZHxu1fVz6r_sZyDel5fn_GN2rSycxRsnOe60000

@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

完。