一文详解Python中PO模式的设计与实现

目录
  • 什么是PO模式
    • PO 三层模式
    • PO 设计模式的优点
  • 将改写的脚本转为PO设计模式
    • 构建基础的 BasePage 层
    • 构建首页的 Page 层(HomePage)
    • 构建登录页的 Page 层(LoginPage)
    • 构建 首页 - 订单 - 支付 流程的 Page 层(OrderPage)
  • PO 设计模式下测试Case的改造

在使用 Python 进行编码的时候,会使用自身自带的编码设计格式,比如说最常见的单例模式,稍微抽象一些的抽象工厂模式等等… 在利用 Python 做自动化测试的时候,是不是也有自己的设计模式呢?所以在今天这个小章节里,需要续了解的就是 python 作为自动化测试里面的一种设计模式,尤其是 UI自动化 的专属模式 —> “PageObject” 自动化设计模式,简称 “PO模式” 。

了解并实现 “PageObject” 自动化设计模式

什么是PO模式

一种在测试自动化中变得流行的设计模式,使得自动化测试脚本的代码量减少,避免代码重复,更加易读,减少维护的成本。

其实简单来说就是将页面的操作、脚本的Case、通用的页面元素分开的这样一个模式。

一般 PO 设计模式多数分为三层

PO 三层模式

第一层:(核心、BasePage层)

  • 对 Selenium 的底层进行二次封装,定义一个所有页面都继承的基础属性页面 —> BasePage 。
  • 封装 Selenium 的基本方法,例如:元素定位、元素等待、导航页面、页面跳转等等...
  • PS:其实在使用的过程中不需要全部封装,用到多少方法就封装多少方法即可。(之前接触过其他大佬的自动化框架,他把所有的 selenium 的底层的方法做了一层封装,这样做很好,能够做很多的事情,但是比较繁重。实际上在真实使用的时候用不到那么多,所以不建议全部封装)。

第二层:(页面层、也叫配置层)

  • 页面元素进行分离,每个元素只定位一次,隔离定位。如果页面改变,只需要改变相应的元素定位。
  • 如果存在一些业务的属性、方法,需要将其通过业务方法的方式将业务与操作元素的动作分离开来。

第三层:(封装测试层)

使用单元测试框架对业务逻辑进行封装测试

PO 设计模式的优点

UI 页面的频繁变化,导致页面 UI 元素频繁的变动,PO设计模式便于元素定位改变的维护。

传统线性自动化,多个用例脚本中需要反复的定位同一个元素,PO设计模式可以减少这部分频繁定位元素的代码量

小节:减少重复代码的冗余,便于UI页面频繁变更下的元素定位维护。

将改写的脚本转为PO设计模式

首先在项目里创建一个 python package 命名为 pages ,然后在 pages 创建一个模块 base_page.py 用来作为第一层的 base_page核心层 。

如下图:

构建基础的 BasePage 层

尝试构建最基础的 base_page 层,代码示例如下:

# coding:utf-8
from selenium import webdriver

class BasePage(object):
    """
    1、第一层 - 核心层-BasePage层,定义一个所有页面都继承的page层
    2、对将要使用的 selenium 的底层方法进行二次封装
    """

    def __init__(self, driver, path=None):     # 构造函数,类的初始化
        """
        为了方便编写将 driver 初始化,
        先使用 "self.driver = webdriver.Chrome()" 后续改为 self.driver = driver
        """
        self.driver = webdriver.Chrome()
        # self.driver = driver
        self.driver.implicitly_wait(5)  # 定义全局的默认加载时间
        self.load_page(path)            # 访问并加载网页

    def load_page(self, path=None):     # 访问并加载网页,如果 path 不为空的话,直接传给 driver.get() 访问
        if path is not None:
            self.driver.get(path)

    def by_xpath(self, xpath):          # 二次封装 selenium 的 xpath 元素定位
        return self.driver.find_element_by_xpath(xpath)

    def js_click(self, xpath):          # JavaScript 定位元素,并执行 click
        self.driver.execute_script('arguments[0].click()', self.by_xpath(xpath))

到这里,base_page 层算是写完了,这就是一个最底层、最基础的类,这个类让我们实现了 selenium 底层的 Xpath 定位方法 与 JavaScript 定位元素方法,这些方法能够帮助我们更好的去完成后续的定位处理操作。

ok,接下我们再去编写各个页面层的东西。

构建首页的 Page 层(HomePage)

代码示例如下:

# coding:utf-8

from selenium import webdriver
from pages.base_page import BasePage    # 导入 base_page 层

class HomePage(BasePage):      # 定义 FirstPage(继承 BasePage )
    """
    1、第二层 - 各个页面单独封装成层,页面的元素、操作、流程
    """
    def direct_to_login(self):      # 首页跳转至登录页
        return self.by_xpath("//*[@id='app']/div[1]/div[5]/div[3]")

    def direct_to_product(self):    # 登陆成功后,跳转至首页
        return self.by_xpath("//*[@id='app']/div[1]/div[5]/div[1]")

    # 方法流程
    def cross_to_login(self):
        self.direct_to_login().click()  # 点击 "登录" 按钮进行登录

    def cross_to_product(self):
        self.direct_to_product().click()    # 点击 "首页" 跳转至首页

构建登录页的 Page 层(LoginPage)

代码示例如下:

# coding:utf-8
from selenium import webdriver
from pages.base_page import BasePage    # 导入 base_page 层

class LoginPage(BasePage):      # 定义 FirstPage(继承 BasePage )
    """
    1、页面层(登录页) - 各个页面单独封装成层,页面的元素、操作、流程
    """
    def login_username(self):    # 登录页 - 用户名输入框
        return self.by_xpath("//*[@id='app']/div[1]/form/div[1]/div[2]/div/input")

    def login_password(self):    # 登录页 - 密码输入框
        return self.by_xpath("//*[@id='app']/div[1]/form/div[2]/div[2]/div/input")

    def login_button(self):      # 登录页 - 登录按钮
        return self.by_xpath("//*[@id='app']/div[1]/form/div[3]/button")

    # 登录Case
    def login(self, username, password):    # 登录方法,传入 username 与 password
        self.login_username().send_keys(username)
        self.login_password().send_keys(password)
        self.login_button().click()

构建 首页 - 订单 - 支付 流程的 Page 层(OrderPage)

# coding:utf-8

from time import sleep
from pages.base_page import BasePage    # 导入 base_page 层

class OrderPage(BasePage):      # 定义 FirstPage(继承 BasePage )
    """
    1、页面层(登录页) - 各个页面单独封装成层,页面的元素、操作、流程
    """
    def product(self):    # 下单 - 第一个产品
        return self.by_xpath("//*[@id='app']/div[1]/div[4]/div[2]/a[1]")

    def ticket_book(self):  # 门票 - 预定(按钮)
        return self.by_xpath("//*[@id='app']/div[1]/div[5]/div[2]/div[2]/a")

    def book_date(self):    # 门票 - 选择日期
        return self.by_xpath("//*[@id='app']/div[1]/form/div[1]/div[1]/div[2]/div/input")

    def to_order(self):     # 门票下单
        return self.by_xpath("//*[@id='app']/div[1]/form/div[4]/div/button")

    def pay_off(self):      # 门票下单 - 支付
        return self.by_xpath("//*[@id='app']/div[1]/form/div/div/button")

    def confirm(self):      # 门票下单 - 确认支付
        return self.by_xpath("/html/body/div[5]/div[3]/button[2]")

    # 下单成功Case
    def place_order(self):
        self.product().click()
        self.ticket_book().click()
        self.book_date().send_keys("2022-06-16")
        self.to_order().click()
        sleep(2)
        element = self.pay_off()
        self.driver.execute_script('arguments[0].click()', element)
        sleep(2)

以上,我们准备的所有页面需要准备的元素定位、基线流程算是写完了,但是具体的用例,应该如何实现呢?继续往下看。

PO 设计模式下测试Case的改造

代码示例如下:

# coding:utf-8

import unittest
from time import sleep
from selenium import webdriver
from pages.home_page import HomePage
from pages.login_page import LoginPage
from pages.order_page import OrderPage

'''
1、初始化 - 打开浏览器,设置浏览器大小
2、最终操作 - 关闭浏览器
3、用例部分 - 登录 与 购买操作、下订单、支付
'''

class TestTravel(unittest.TestCase):
    @classmethod
    def setUpClass(cls):                    # 每个测试类在加载之前执行一次 setUpClass ,初始化方法
        cls.driver = webdriver.Chrome()
        cls.driver.maximize_window()

    def test_a_order(self):
        #初始化参数
        username = '13500000001'
        password = 'Success@2020'

        #初始化界面
        home_page = HomePage(driver=self.driver, path="http://django.t.mukewang.com/#/")
        login_page = LoginPage(driver=self.driver)
        order_page = OrderPage(driver=self.driver)

        #跳转登录
        home_page.cross_to_login()

        #登录
        login_page.login(username, password)

        # 跳转至订单页
        home_page.cross_to_product()

        #下单
        order_page.place_order()

    @classmethod
    def tearDownClass(cls):
        cls.driver.quit()       # 彻底退出浏览器

if __name__ == '__main__':
    unittest.main()

这里改造完成之后,记得将 "BasePage 层" 的 '# self.driver = driver' 取消注释,并将 'self.driver = webdriver.Chrome()' 注释掉 。

以上就是一个比较完整的通过 PO 的方式来连接三个页面与基础的 base_page 来写出的更简洁一些的测试用例。

运行结果如下:(速度可能过快,担待一下,gif 只有15秒的时间)

到此这篇关于一文详解Python中PO模式的设计与实现的文章就介绍到这了,更多相关Python PO模式内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • PO模式在selenium自动化测试框架的优势

    PO模式简介 1.什么是PO模式 PO模型是:Page Object Model的简写 页面对象模型 作用:就是把测试页面和测试脚本进行分离,即把页面封装成类,供测试脚本进行调用; 分层机制,让不同层去做不同类型的事情,让代码结构清晰,增加复用性. PO设计模式是Selenium自动化测试中最佳的设计模式之一,主要体现在对界面交互细节的封装 2. 不使用PO设计会出现以下几种情况: 复用性不太好,扩展性不好,易读性差,不好维护,UI界面频繁的项目维护起来比较麻烦. 3.PO模式的优缺点 优点:

  • Python PO设计模式的具体使用

    无规矩不成方圆.编写代码也是,如果没有大概的框架,管理代码将会是一件很头疼的事. 先看看笔者以前写的python脚本: 如果只有一个用例,这样看着好像挺整洁的.但是当用例越来越多后,如果元素定位发生了改变,那你将要在多个类.多个方法中,去寻找那个元素,然后一个一个修改,这将耗费很多时间. 引入PO设计模式后,管理代码将会很轻松. 什么是PO设计模式? PO设计模式是一种业务流程与页面元素操作分离的模式:这意味着,当UI发生变化,元素定位发生变化时,只需要在一个地方修改即可. 下面是代码目录: 页

  • 一文详解Python中PO模式的设计与实现

    目录 什么是PO模式 PO 三层模式 PO 设计模式的优点 将改写的脚本转为PO设计模式 构建基础的 BasePage 层 构建首页的 Page 层(HomePage) 构建登录页的 Page 层(LoginPage) 构建 首页 - 订单 - 支付 流程的 Page 层(OrderPage) PO 设计模式下测试Case的改造 在使用 Python 进行编码的时候,会使用自身自带的编码设计格式,比如说最常见的单例模式,稍微抽象一些的抽象工厂模式等等… 在利用 Python 做自动化测试的时候,

  • 一文详解Python中复合语句的用法

    目录 Python复合语句 1.if 语句 2.while 语句 3.for 语句 4.try 语句 5.with 语句 6.match 语句 Python复合语句 复合语句是包含其它语句(语句组)的语句:它们会以某种方式影响或控制所包含其它语句的执行.通常,复合语句会跨越多行,虽然在某些简单形式下整个复合语句也可能包含于一行之内. if.while和for语句用来实现传统的控制流程构造.try语句为一组语句指定异常处理和/和清理代码,而with语句允许在一个代码块周围执行初始化和终结化代码.函

  • 一文详解Python中生成器的原理与使用

    目录 什么是生成器 迭代器和生成器的区别 创建方式 生成器表达式 基本语法 生成器函数 yield关键字 yield和return yield的使用方法 生成器函数的基本使用 send的使用 可迭代对象的优化 总结 我们学习完推导式之后发现,推导式就是在容器中使用一个for循环而已,为什么没有元组推导式? 原因就是“元组推导式”的名字不是这样的,而是叫做生成器表达式. 什么是生成器 生成器表达式本质上就是一个迭代器,是定义迭代器的一种方式,是允许自定义逻辑的迭代器.生成器使用generator表

  • 一文详解Python中的重试机制

    目录 介绍 1. 最基本的重试 2. 设置停止基本条件 3. 设置何时进行重试 4. 重试后错误重新抛出 5. 设置回调函数 介绍 为了避免由于一些网络或等其他不可控因素,而引起的功能性问题.比如在发送请求时,会因为网络不稳定,往往会有请求超时的问题. 这种情况下,我们通常会在代码中加入重试的代码.重试的代码本身不难实现,但如何写得优雅.易用,是我们要考虑的问题. 这里要给大家介绍的是一个第三方库 - Tenacity (标题中的重试机制并并不准确,它不是 Python 的内置模块,因此并不能称

  • 一文详解Python中的Map,Filter和Reduce函数

    目录 1. 引言 2. 高阶函数 3. Lambda表达式 4. Map函数 5. Filter函数 6. Reduce函数 7. 总结 1. 引言 本文重点介绍Python中的三个特殊函数Map,Filter和Reduce,以及如何使用它们进行代码编程.在开始介绍之前,我们先来理解两个简单的概念高阶函数和Lambda函数. 2. 高阶函数 把函数作为参数传入,这样的函数称为高阶函数,函数式编程就是指这种高度抽象的编程范式. 举例如下: def higher(your_function, som

  • 一文详解Python中实现单例模式的几种常见方式

    目录 Python 中实现单例模式的几种常见方式 元类(Metaclass): 装饰器(Decorator): 模块(Module): new 方法: Python 中实现单例模式的几种常见方式 元类(Metaclass): class SingletonType(type): """ 单例元类.用于将普通类转换为单例类. """ _instances = {} # 存储单例实例的字典 def __call__(cls, *args, **kwa

  • 一文详解Python中logging模块的用法

    目录 一.低配logging 1.v1 2.v2 3.v3 二.高配logging 1.配置日志文件 2.使用日志 三.Django日志配置文件 一.低配logging 日志总共分为以下五个级别,这个五个级别自下而上进行匹配 debug-->info-->warning-->error-->critical,默认最低级别为warning级别. 1.v1 import logging logging.debug('调试信息') logging.info('正常信息') logging

  • 详解Python中的编码问题(encoding与decode、str与bytes)

    1 引言 在文件读写及字符操作时,我们经常会出现下面这几种错误: TypeError: write() argument must be str, not bytes AttributeError: 'URLError' object has no attribute 'code' UnicodeEncodeError: 'gbk' codec can't encode character '\xa0' inposition 5747: illegal multibyte sequence 这些

  • 详解Python中pyautogui库的最全使用方法

    在使用Python做脚本的话,有两个库可以使用,一个为PyUserInput库,另一个为pyautogui库.就本人而言,我更喜欢使用pyautogui库,该库功能多,使用便利.下面给大家介绍一下pyautogui库的使用方法.在cmd命令框中输入pip3 install pyautogui即可安装该库! 常用操作 我们在pyautogui库中常常使用的方法,如下: import pyautogui pyautogui.PAUSE = 1 # 调用在执行动作后暂停的秒数,只能在执行一些pyaut

  • 详解python中GPU版本的opencv常用方法介绍

    引言 本篇是以python的视角介绍相关的函数还有自我使用中的一些问题,本想在这篇之前总结一下opencv编译的全过程,但遇到了太多坑,暂时不太想回看做过的笔记,所以这里主要总结python下GPU版本的opencv. 主要函数说明 threshold():二值化,但要指定设定阈值 blendLinear():两幅图片的线形混合 calcHist() createBoxFilter ():创建一个规范化的2D框过滤器 canny边缘检测 createGaussianFilter():创建一个Ga

随机推荐