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阅读 203,772评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,458评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,610评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,640评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,657评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,590评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,962评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,631评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,870评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,611评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,704评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,386评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,969评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,944评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,179评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,742评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,440评论 2 342

推荐阅读更多精彩内容

  • 一、PO模型介绍 在自动化中,Selenium自动化测试中有一个名字经常被提及PageObject(思想与面向对象...
    故里里阅读 262评论 0 1
  • 一、PO模型介绍在自动化中,Selenium自动化测试中有一个名字经常被提及PageObject(思想与面向对象的...
    老友_9e40阅读 176评论 0 0
  • 一、PO模型介绍 在自动化中,Selenium自动化测试中有一个名字经常被提及PageObject(思想与面向对象...
    ln111阅读 264评论 0 0
  • 1.介绍 在自动化中,Selenium自动化测试中有一个名字经常被提及PageObject(思想与 面向对象的特征...
    我向星明许愿阅读 189评论 0 0
  • 一. PO模型介绍 优点:重用,业务和对象分离,代码结构清晰,方便代码维护 二、PO的核心要素 1.在PO模式中抽...
    BnAO阅读 424评论 0 0