PO

PO:Page Object,po是一種設計模式,提供通用的方法,簡單來說就是分層設計。
場景:我們最初做UI自動化測試時,一個python文件可以實現整個流程的操作,那么會帶來的問題有:
  • 代碼可讀性差,一旦有一個元素的位置發生改動,改起來特別費勁;
  • 代碼維護性差,時間久了看不懂,別人也看不懂;
  • 可擴展性弱,比如登錄,這個頁面需要登錄,到了另外一個頁面可能還需要登錄,需要再寫一遍。

要解決這種問題,我們需要把場景抽象化,通過PO思想進行封裝。把操作細節封裝成方法,對外只提供方法,不提供細節。當需要修改時只改操作細節,不改對外暴露的方法。但是,不是所有頁面都封裝,只封裝主要模塊,不封裝次要模塊。


截圖.png
PO原則
  • 用一個方法代替頁面的服務
  • 對外提供的方法不暴露細節
  • 對外提供的方法中不包含斷言
  • 如果頁面a導航到頁面b,page a應該return page b
  • 不為頁面的所有元素封裝方法
  • 當出現正確的和錯誤的結果時,我們封裝時要分開寫,不要揉到一起寫

舉個例子,以企業微信后臺為例,先構思一下用例:


image.png

用PO思想,我需要創建幾個文件:

  • main_page.py:主頁的po,提供主頁的方法,不提供細節
  • base_page.py:抽離通用方法的po
  • add_member_page.py:添加成員頁po,繼承base_page,對應具體添加成員的操作細節
  • contact_page.py:通訊錄頁的po,繼承base_page,對應具體通訊錄的操作細節
  • test_add_member.py:添加成員的測試用例及斷言

main_page.py

from time import sleep
from selenium import webdriver
from selenium.webdriver.common.by import By
from test_selenium.work_weixin.api.add_member_page import AddMemberPage
from test_selenium.work_weixin.api.base_page import BasePage
from test_selenium.work_weixin.api.contact_page import ContactPage

class MainPage(BasePage):
    """
    首頁
    """
    _base_url = 'https://work.weixin.qq.com/wework_admin/frame#index'
    def goto_contact(self):
        """
        跳轉通訊錄頁面
        :return:
        """
        return ContactPage()

    def goto_add_member(self):
        """
        跳轉添加成員頁面
        :return:
        """
        self.driver.find_element(By.XPATH, '//*[@id="_hmt_click"]/div[1]/div[4]/div[2]/a[1]/div/span[2]').click()
        sleep(1)
        return AddMemberPage(self.driver)

base_page.py

from time import sleep
import yaml
from selenium import webdriver
from selenium.webdriver.chrome.webdriver import WebDriver
from selenium.webdriver.opera.options import Options

class BasePage:
    """
    封裝所有頁面對象通用的操作,如driver的實例化
    """
    _base_url = None
    def __init__(self, base_driver=None):
        """
        子類沒有構造函數,在實例化過程中會自動調用父類的構造函數,所以每個PO在實例化過程中都會執行構造函數的邏輯,這樣就會初始化多個driver
        所以需要避免driver的重復實例化
        """
        # 如果base_driver為真,就不需要重復driver實例化操作
        if base_driver:
            self.driver =base_driver
        # 如果base_driver為None,就需要對driver實例化
        else:
            # 不加self是局部變量
            self.driver = webdriver.Chrome()
            self.driver.implicitly_wait(5)
            # url優化
            if self._base_url != None:
                self.driver.get(self._base_url)
                cookie = yaml.safe_load(open('../data/login_cookie.yml'))
                print(cookie)
                # 種cookie
                for c in cookie:
                    self.driver.add_cookie(c)
                sleep(2)
                self.driver.get(self._base_url)

    def find(self, by, locaotor):
        """
        封裝find_element操作
        :param by:
        :param locaotor:
        :return:
        """
        res = self.driver.find_element(by, locaotor)
        print(f"找到的元素為{res}")
        return res

add_member_page.py

from time import sleep
from selenium.webdriver.chrome.webdriver import WebDriver
from selenium.webdriver.common.by import By
from test_selenium.work_weixin.api.base_page import BasePage
from test_selenium.work_weixin.api.contact_page import ContactPage

class AddMemberPage(BasePage):
    """
    添加成員頁
    """
    _base_url = 'https://work.weixin.qq.com/wework_admin/frame#contacts'

    def goto_contact(self):
        """
        跳轉通訊錄頁面
        :return:
        """
        return ContactPage()

    def add_member(self, username, acctid, phonenum):
        """
        添加成員操作
        :return:
        """
        self.find(By.ID, 'username').send_keys(username)
        self.find(By.ID, 'memberAdd_acctid').send_keys(acctid)
        self.find(By.ID, 'memberAdd_phone').send_keys(phonenum)
        self.driver.find_element(By.CSS_SELECTOR, '.js_btn_save').click()
        return ContactPage(self.driver)

    def add_member_fail(self):
        """
        添加成員操作
        :return:
        """
        self.find(By.ID, 'username').send_keys('')
        self.find(By.ID, 'memberAdd_acctid').send_keys('111')
        self.find(By.ID, 'memberAdd_phone').send_keys('13600001243')
        error_list = self.driver.find_elements(By.CSS_SELECTOR, '.ww_inputWithTips_tips')
        print(error_list)

        # 尋找所有的錯誤信息,如果不為空,則返回
        err_message = [ele.text for ele in error_list if ele.text != ""]
        return err_message
        return ContactPage(self.driver)

contact_page.py

from test_selenium.work_weixin.api.base_page import BasePage
class ContactPage(BasePage):
    """
    通訊錄列表頁
    """
    _base_url = 'https://work.weixin.qq.com/wework_admin/frame#contacts'

    def goto_add_member(self):
        """
        添加成員
        :return:
        """
        # 如果出現A到B,B到A循環導入的場景,需要把其中一個的導包放在方法里
        from test_selenium.work_weixin.api.add_member_page import AddMemberPage

        return AddMemberPage()

    def member_list(self, member_name):
        """
        成員列表list
        :return:
        """
        return ['member_name']

test_add_member.py

from test_selenium.work_weixin.api.add_member_page import AddMemberPage
from test_selenium.work_weixin.api.contact_page import ContactPage
from test_selenium.work_weixin.api.main_page import MainPage

class TestAddMember:
    """
    添加成員的測試用例
    """
    def setup_class(self):
        """
        頁面實例化MainPage
        :return:
        """
        self.main = MainPage()

    def test_add_member(self):
        """
        1.在首頁,點擊添加成員按鈕,跳轉到添加成員頁面
        2.在添加成員頁面,填寫成員信息,點擊保存
        3.在通訊錄頁面查看信息是否添加成功

        :return:
        """
        # # 方法一
        # # 頁面實例化,調用過于復雜
        # main = MainPage()
        # add_member_page = AddMemberPage()
        # contact_page = ContactPage()
        #
        # # 1.在首頁,點擊添加成員按鈕,跳轉到添加成員頁面
        # main.goto_add_member()
        #
        # # 2在添加成員頁面,填寫成員信息,點擊保存
        # add_member_page.add_member()
        #
        # # 3.在通訊錄頁面查看成員是否添加成功
        # contact_page.member_list()

        # 方法二
        username, acctid, phonenum = "吱吱3", '3', '18700001234'

        # 通過前面return實例對象,可以直接用方法.方法的方式,實現鏈式調用
        res = self.main.goto_add_member().add_member(username, acctid, phonenum).member_list('member_name')

        # 斷言與手工測試一致,斷言添加的成員是否在結果中,如果在表示用例通過,否則失敗
        assert 'member_name' in res

    def test_add_member_fail(self):
        """
        反向用例,成員名稱為空
        :return:
        """
        error_list = self.main.goto_add_member().add_member_fail()
        assert "請填寫姓名" in error_list

    def teardown_class(self):
        self.main.driver.quit()

源碼彩蛋》》》
鏈接: https://pan.baidu.com/s/14p0Q38whTbjlRwlMZA-iVg
提取碼: 7wzh

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,363評論 6 532
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,497評論 3 416
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,305評論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,962評論 1 311
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,727評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,193評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,257評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,411評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,945評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,777評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,978評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,519評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,216評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,642評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,878評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,657評論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,960評論 2 373

推薦閱讀更多精彩內容

  • 一、PO模型介紹 在自動化中,Selenium自動化測試中有一個名字經常被提及PageObject(思想與面向對象...
    故里里閱讀 285評論 0 1
  • 一、PO模型介紹在自動化中,Selenium自動化測試中有一個名字經常被提及PageObject(思想與面向對象的...
    老友_9e40閱讀 184評論 0 0
  • 一、PO模型介紹 在自動化中,Selenium自動化測試中有一個名字經常被提及PageObject(思想與面向對象...
    ln111閱讀 272評論 0 0
  • 1.介紹 在自動化中,Selenium自動化測試中有一個名字經常被提及PageObject(思想與 面向對象的特征...
    我向星明許愿閱讀 202評論 0 0
  • 一. PO模型介紹 優點:重用,業務和對象分離,代碼結構清晰,方便代碼維護 二、PO的核心要素 1.在PO模式中抽...
    BnAO閱讀 462評論 0 0