UI自動化學習筆記- PO模型介紹和使用


一、PO模型

1、PO介紹:page(頁面) object(對象)

在自動化中,Selenium 自動化測試中有一個名字經常被提及 PageObject (思想與面向對象的特征相同),通常PO 模型可以大大提高測試用例的維護效率。

優點:
提交測試腳本可讀性
減少代碼重復
提高測試用例的可維護性,特別是針對UI變動頻繁的項目

缺點:
結構復雜:基於流程進行了模塊化的拆分

結構:

  1. base(基類)
  2. page(頁面對象)
  3. scripts(業務層)

擴展:
loc 變量:類型為元組:*loc為解包

2、PageObject 設計模式

3、PO 的核心要素

  1. 在 PO 模式中抽離封裝集成一個 BasePage 類,該基類應該擁有一個只實現 webdriver 實例的屬性

  2. 每一個page 都繼承BasePage,通過 driver 來管理 page 中元素,將 page 中的操作封裝成一個個方法

  3. TestCase 繼承 unittest.TestCase類,並依賴page類,從而實現相應的測試步驟

二、將Selenium代碼封裝成PO模型

1、案例說明(簡單的登錄測試用例)

(1)改造案例思路:

  • 第一, 我們要分離測試對象(元素對象)和測試腳本(用例腳本),那么我們分別創建兩個腳本文件,分別為:
    • LoginPage.py 用於定義頁面元素對象,每一個元素都封裝成組件(可以看做存放頁面元素對象的倉庫)
    • TestCaseLogin.py 測試用例腳本。
  • 第二,抽取出公共方法定義在base.py文件中,每個Page類都要繼承這個base.py文件,也就是每Page類都能使用base類中的方法,來操作頁面中的元素,同時也可以在每個Page類中定義自己獨有的方法,解決工作中的實際需求。
  • 第三,設計實現思想,一切元素和元素的操作組件化定義在Page頁面,用例腳本頁面,通過調用Page中的組件對象,進行拼湊成一個登錄腳本。

(2)封裝公共操作在base類(base.py)

把一些公共的方法放到此類中,這個類將被PO對象繼承。

from selenium.common.exceptions import NoSuchElementException
from selenium.webdriver import ActionChains
from selenium.webdriver.support.wait import WebDriverWait


class Base(object):

    def __init__(self, driver):
        self.driver = driver

    # 查找元素方法(提供:點擊、輸入、獲取文本)使用
    def base_find_element(self, loc, timeout=30, poll=0.5):
        element, index = loc[:2], loc[-1]
        try:
            ele = WebDriverWait(
                self.driver, timeout=timeout, poll_frequency=poll
            ).until(
                lambda x: x.find_elements(*element)
            )[index]
        except (NoSuchElementException, TimeoutError, IndexError):
            return False
        else:
            return ele

    # 點擊方法
    def base_click(self, loc):
        self.base_find_element(loc).click()

    # 輸入方法
    def base_input(self, loc, value):
        el = self.base_find_element(loc)
        # 清空
        el.clear()
        # 輸入
        el.send_keys(value)

    # 獲取文本方法
    def base_get_text(self, loc):
        return self.base_find_element(loc).text

    # 獲取當前頁面地址
    def base_get_current_url(self):
        return self.driver.current_url

    # 鼠標懸停事件
    def base_move_to_element(self, loc):
        ActionChains(self.driver).move_to_element(loc).perform()

(3)每個頁面對應一個Page類(login_page.py)

定位元素的定位器和操作元素方法分離開,元素定位器全部放一起,然后每一個操作元素動作寫成一個方法。

from selenium.webdriver.common.by import By
from public.base import Base


login_username = (By.CLASS_NAME, 'el-input__inner', 1)
login_password = (By.CLASS_NAME, 'el-input__inner', 2)
login_btn = (By.CLASS_NAME, 'login__button', 0)
organ_name = (By.CSS_SELECTOR, '.role-names-button', 0)
logout_btn = (By.CLASS_NAME, 'logout', 0)
password_login_text = (By.CSS_SELECTOR, '.pwd-login', 0)


class LoginPage(object):

    def __init__(self, driver):
        self.element = Base(driver)

    # 輸入手機號
    def input_username(self, content):
        self.element.base_input(login_username, content)

    # 輸入密碼
    def input_password(self, content):
        self.element.base_input(login_password, content)

    # 點擊登錄
    def click_login_button(self):
        self.element.base_click(login_btn)

    # 獲取當前頁面url
    def get_current_url(self):
        return self.element.base_get_current_url()

    # 點擊機構名稱
    def click_to_organ_name(self):
        self.element.base_click(organ_name)

    # 點擊退出
    def click_logout(self):
        self.element.base_click(logout_btn)

    # 獲取“密碼登錄”文字
    def get_password_login_text(self):
        return self.element.base_get_text(password_login_text)

(4)封裝業務層(login_business.py)

import time

from Test.Page.login_page import *


class LoginBusiness(object):

    def __init__(self, driver):
        self.login_business = LoginPage(driver)

    # 登錄系統
    def go_system(self, username, password):
        self.login_business.input_username(username)
        self.login_business.input_password(password)
        self.login_business.click_login_button()
        time.sleep(2)
        return self.login_business.get_current_url()

    # 退出成功
    def logout(self):
        self.login_business.click_to_organ_name()
        self.login_business.click_logout()
        return self.login_business.get_password_login_text()

(4)原登陸案例封裝完成代碼

測試方法及測試類的執行都在此文件中。

from Test.Case.base_case import LoginBaseCase
from public.get_log import LogInfo
from public.read_ini import get_value
from Test.Business.login_business import LoginBusiness


class TestLogin(LoginBaseCase, LogInfo):
    """ 登錄退出測試用例 """

    @classmethod
    def setUpClass(cls) -> None:
        cls.case_forward()
        LogInfo().log.info('TestLogin Cases Suite Start Running')
        cls.login = LoginBusiness(cls.driver)

    @LogInfo.get_error
    def test_1(self):
        """ 登錄流程 """
        self.log.info('TestCase1 Start Running')
        # 獲取用戶名密碼
        username = get_value(get_value('Base', 'Env'), 'username')
        password = get_value(get_value('Base', 'Env'), 'password')
        url = self.login.go_system(username, password)
        text = "https://gssdev.haoshengy.com/pc_workbench/workbench/overview"
        self.assertEqual(url, text, '當前頁面URL不正確--測試不通過')

    def test_2(self):
        """ 退出登錄 """
        self.log.info('TestCase2 Start Running')
        text = self.login.logout()
        self.assertEqual("密碼登錄", text, '首頁密碼登錄字樣錯誤--測試不通過')

三、數據驅動

1.1 什么是數據驅動

數據驅動:是以數據來驅動整個測試用例的執行,也就是測試數據決定測試結果

1.2 數據驅動的特點
  1. 數據驅動本身不是一個工業級標磚的概念,因此在不同的公司都會有不同的解釋
  2. 可以把數據驅動理解成一種模式或者一種思想
  3. 數據驅動技術可以讓用戶把關注點放在測試數據的構建和維護上,而不是直接維護腳本,可以利用同樣的過程對不同的數據輸入進行測試
  4. 數據驅動的實現要依賴參數化的技術
1.3 傳入數據的方式(測試數據的來源)
  1. 直接定義在測試腳本中(簡單直觀,但代碼和數據未實現真正的分離,不方便后期維護)
  2. 從文件讀取數據,如json、Excel、xml、txt、等格式文件
  3. 從數據庫中讀取數據
  4. 直接調用接口獲取數據源
  5. 本地封裝一些生成數據的方法


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM