PO模式

簡介:PO模式(Page Object), 是自動化測試中最為流行且最為熟悉和推崇的一種設計模式。PO模式把頁面元素和元素的操作方法按照頁面抽象出來收壕,分離成一定的對象,然后再進行組織遭居。

介紹

做web自動化最頭疼的問題是頁面變化了啼器,如果沒有使用PO設計模式,頁面一變化就意味著之前寫的元素定位不能用了俱萍,需要重新修改端壳,這樣就需要一個個從測試腳本中把需要修改的元素定位找出來,然后一一修改枪蘑。自動化腳本不但繁瑣损谦,而且成本極高。

PO模式就可以很好地解決這個問題岳颇,那如何操作呢照捡?

一般對腳本進行分層:

  • 基礎層:點擊、輸入等操作加入一些等待话侧、日志輸入栗精、截圖等操作,方便以后查看腳本的運行情況及問題排查
  • 對象邏輯層:用于存放頁面元素定位和存放一些封裝好對功能用例模塊
  • 業(yè)務層:用于存放真正的測試用例的操作部分

優(yōu)點:

  • 減少代碼冗余
  • 業(yè)務和實現(xiàn)分離
  • 降低維護成本

基礎層

后續(xù)的對象層操作元素時都繼承這個基礎類。

'''
Author: daju.huang
Date: 2021-07-22 15:40:38
LastEditors: daju.huang
LastEditTime: 2021-07-25 12:59:58
FilePath: /webUIAutoTest/public/page_obj/base.py
'''

import os,sys
from time import sleep
sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(__file__))))
from config import setting
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.common.exceptions import NoSuchFrameException,NoSuchWindowException,NoAlertPresentException,NoSuchElementException
import configparser
from public.models.log import Log


log = Log()

class Page(object):
    
    '''
    @description: 基礎類悲立,用于頁面對象類的繼承
    @param {*} self 
    @param {*} selenium_driver
    @param {*} base_url
    @param {*} parent
    @return {*}
    '''    
    def __init__(self,selenium_driver,base_url,parent=None) -> None:
        self.driver = selenium_driver
        self.base_url = base_url
        self.parent = parent
        self.timeout = 10
    
    '''
    @description: url地址判斷是否是當前頁面
    @param {*} self
    @return {*}
    '''    
    def on_page(self):
        return self.driver.current_url == (self.base_url + self.url)
        
    '''
    @description: 打開瀏覽器URL訪問
    @param {*} self
    @param {*} url URL地址
    @return {*}
    '''        
    def _open(self,url):
        url = self.base_url + url
        self.driver.get(url)
        assert self.on_page(),'Did not land on %s' % url
        
    '''
    @description: 內(nèi)部調(diào)用_open私有函數(shù)
    @param {*} self
    @return {*}
    '''        
    def open(self):
        self._open(self.url)
    
    '''
    @description:  單個元素定位
    @param {*} self
    @param {array} loc 傳入元素屬性
    @return {*} 定位到的元素
    '''    
    def find_element(self, *loc):
        try:
            WebDriverWait(self.driver, self.timeout).until(EC.visibility_of_element_located(loc))
            return self.driver.find_element(*loc)
        except:
            log.error("{0}頁面中未能找到{1}元素".format(self,loc))
    
    '''
    @description: 多個元素定位
    @param {*} self
    @param {array} loc 傳入元素屬性
    @return {*} 定位到的元素
    '''    
    def find_elements(self, *loc):
        try:
            WebDriverWait(self.driver, self.timeout).until(EC.visibility_of_element_located(loc))
            return self.driver.find_elements(*loc)
        except:
            log.error("{0}頁面中未能找到{1}元素".format(self,loc))
    
    '''
    @description: 單個元素定位
    @param {*} self
    @param {array} loc 傳入元素屬性
    @return {*} 定位到的元素
    '''    
    def find_element_xpath(self, loc_str):
        try:
            return self.driver.find_element_by_xpath(loc_str)
        except:
            log.error("{0}頁面中未能找到{1}元素".format(self,loc_str))
    
    '''
    @description: 提供調(diào)用JavaScript方法
    @param {*} self
    @param {*} src 腳本文件
    @return {*} JavaScript腳本
    '''    
    def script(self,src):
        return self.driver.execute_script(src)
    
    '''
    @description: 
    @param {*} self
    @param {*} loc
    @param {*} value
    @param {*} clear_first
    @param {*} click_first
    @return {*}
    '''    
    def send_key(self, loc, value, clear_first=True, click_first=True):
        try:
            loc = getattr(self, "_%s" % loc)   # getattr相當于實現(xiàn)self.loc
            if click_first:
                self.find_element(*loc).click()
            if clear_first:
                self.find_element(*loc).clear()
                self.find_element(*loc).send_keys(value)
        except AttributeError:
            log.error("%s 頁面中未能找到 %s 元素" % (self, loc))
    
    '''
    @description: 多表單嵌套切換
    @param {*} self
    @param {*} loc 傳元素的屬性值
    @return {*} 定位到的元素
    '''    
    def switch_frame(self, loc):
        try:
            return self.driver.switch_to_frame(loc)
        except NoSuchFrameException as msg:
            log.error("查找iframe異常->{0}".format(msg))

    '''
    @description: 多窗口切換
    @param {*} self
    @param {*} loc
    @return {*}
    '''    
    def switch_windows(self,loc):
        try:
            return self.driver.switch_to_window(loc)
        except NoSuchWindowException as msg:
            log.error("查找窗口句柄handle異常->{0}".format(msg))
    
    '''
    @description: 警告框處理
    @param {*} self
    @return {*}
    '''    
    def switch_alert(self):
        try:
            return self.driver.switch_to_alert()
        except NoAlertPresentException as msg:
            log.error("查找alert彈出框異常->{0}".format(msg))

對象邏輯層

存放頁面元素定位和元素操作方法鹿寨,實現(xiàn)一個頁面一個模塊,后續(xù)頁面元素發(fā)生變化薪夕,只需要修改這個模塊中對應的定位表達式或者操作方法即可脚草。

        # baidu_page.py

from selenium.webdriver.common.by import By
from common.basepage import BasePage


class LoginPage(Base):

    login_btn = (By.XPATH, '//div[@id="u1"]//a[@name="tj_login"]')  # 登錄按鈕
    username_login_btn = (By.ID, 'TANGRAM__PSP_11__footerULoginBtn')    # 用戶名登錄按鈕
    user_input = (By.ID, 'TANGRAM__PSP_11__userName')  # 用戶信息輸入框
    pwd_input = (By.ID, 'TANGRAM__PSP_11__password')  # 密碼輸入框
    login_submit = (By.ID, 'TANGRAM__PSP_11__submit')   # 登錄提交按鈕
        
    """
     百度用戶名登錄
     :param user: 手機/郵箱/用戶名
     :param pwd: 密碼
     :return:
    """
    def login(self, user, pwd):
        self.click_element(self.login_btn, '百度-登錄')
        self.click_element(self.username_login_btn, '百度登錄-用戶名登錄')
        self.input_text(self.user_input, user, '用戶名登錄-手機/郵箱/用戶名')
        self.input_text(self.pwd_input, pwd, '用戶名登錄-密碼')
        self.click_element(self.login_submit, '用戶名登錄-登錄')

業(yè)務層

用于存放真正的測試用例操作,這里不會出現(xiàn)元素定位原献、頁面功能馏慨,所有操作都是直接調(diào)用邏輯層的。

import unittest
import pytest
import ddt
from selenium import webdriver
from PageObjects.baidu_login_page import LoginPage
from testdatas import common_datas as com_d
from testdatas import login_data as lo_d
from common.logging import log


@ddt.ddt
class TestLogin(unittest.TestCase):

    def setUp(self):
        log.info("-------用例前置工作:打開瀏覽器--------")
        self.driver = webdriver.Chrome()
        self.driver.get(com_d.baidu_url)
        self.driver.maximize_window()

    def tearDown(self):
        self.driver.quit()
        log.info("-------用例后置工作:關閉瀏覽器--------")

    @pytest.mark.smoke
    def test_login_success(self):
        # 用例:登錄頁的登錄功能
        # 步驟
        LoginPage(self.driver).login(lo_d.success_data['user'], lo_d.success_data['pwd'])
        # 斷言.....
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末姑隅,一起剝皮案震驚了整個濱河市写隶,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌粤策,老刑警劉巖樟澜,帶你破解...
    沈念sama閱讀 216,591評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異叮盘,居然都是意外死亡秩贰,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,448評論 3 392
  • 文/潘曉璐 我一進店門柔吼,熙熙樓的掌柜王于貴愁眉苦臉地迎上來毒费,“玉大人,你說我怎么就攤上這事愈魏∶俨#” “怎么了?”我有些...
    開封第一講書人閱讀 162,823評論 0 353
  • 文/不壞的土叔 我叫張陵培漏,是天一觀的道長溪厘。 經(jīng)常有香客問我,道長牌柄,這世上最難降的妖魔是什么畸悬? 我笑而不...
    開封第一講書人閱讀 58,204評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮珊佣,結果婚禮上蹋宦,老公的妹妹穿的比我還像新娘。我一直安慰自己咒锻,他們只是感情好冷冗,可當我...
    茶點故事閱讀 67,228評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著惑艇,像睡著了一般蒿辙。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,190評論 1 299
  • 那天须板,我揣著相機與錄音碰镜,去河邊找鬼。 笑死习瑰,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的秽荤。 我是一名探鬼主播甜奄,決...
    沈念sama閱讀 40,078評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼窃款!你這毒婦竟也來了课兄?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 38,923評論 0 274
  • 序言:老撾萬榮一對情侶失蹤晨继,失蹤者是張志新(化名)和其女友劉穎烟阐,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體紊扬,經(jīng)...
    沈念sama閱讀 45,334評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡蜒茄,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,550評論 2 333
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了餐屎。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片檀葛。...
    茶點故事閱讀 39,727評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖腹缩,靈堂內(nèi)的尸體忽然破棺而出屿聋,到底是詐尸還是另有隱情,我是刑警寧澤藏鹊,帶...
    沈念sama閱讀 35,428評論 5 343
  • 正文 年R本政府宣布润讥,位于F島的核電站,受9級特大地震影響盘寡,放射性物質發(fā)生泄漏楚殿。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,022評論 3 326
  • 文/蒙蒙 一宴抚、第九天 我趴在偏房一處隱蔽的房頂上張望勒魔。 院中可真熱鬧,春花似錦菇曲、人聲如沸冠绢。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,672評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽弟胀。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間孵户,已是汗流浹背萧朝。 一陣腳步聲響...
    開封第一講書人閱讀 32,826評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留夏哭,地道東北人检柬。 一個月前我還...
    沈念sama閱讀 47,734評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像竖配,于是被迫代替她去往敵國和親何址。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,619評論 2 354

推薦閱讀更多精彩內(nèi)容