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

news2024/12/23 13:13:46

在使用 Python 进行编码的时候,会使用自身自带的编码设计格式,比如说最常见的单例模式等。本文将为大家介绍PageObject自动化设计模式(PO模式)的设计与实现,感兴趣的可以了解一下

在使用 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模式内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持小编!

下面是配套资料,对于做【软件测试】的朋友来说应该是最全面最完整的备战仓库,这个仓库也陪伴我走过了最艰难的路程,希望也能帮助到你!

资料获取方式 :

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/856597.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Nginx(3)

目录 1.Nginx虚拟主机1.1基于IP虚拟主机1.2基于端口虚拟主机1.3基于域名实现的虚拟主机 2.日志详解 1.Nginx虚拟主机 虚拟主机,Nginx配置中的多个server{}区域对应不同的业务(站点) 虚拟主机方式基于域名的虚拟主机不同的域名访问不同的站点基于IP的虚拟主机不同的…

纯跟踪(Pure Pursuit)路径跟踪算法研究(1)

纯跟踪(Pure Pursuit)路径跟踪算法研究(1) 下午主要读了几篇论文 《自动泊车路径纯跟踪算法应用研究》 《基于纯追踪算法和樽海鞘优化算法的无人驾驶路径跟踪算法研究》 《基于自适应PP和MPC的智能车辆路径跟踪控制》 首先在公式推导方面还不是很清晰 最…

缓解针对LLM应用程序的存储提示注入攻击

推荐:使用 NSDT场景编辑器 助你快速搭建可编辑的3D应用场景 LLM提供提示文本,并根据其已训练和访问的所有数据进行响应。为了用有用的上下文补充提示,一些 AI 应用程序捕获来自用户的输入,并在将最终提示发送到 LLM 之前将用户看不…

7.5 详解批量规范化 对某个维度取平均值代码解读

一.举例计算均值、方差 假设我们有以下一组数据:[10, 15, 20, 25, 30] 首先,我们计算均值,即将所有数据相加后除以数据的数量: 均值 (10 15 20 25 30) / 5 100 / 5 20 1.1标准差 接下来,我们计算标准差&…

_declspec(naked) 初试(裸函数)

最近在写驱动时候初次使用裸函数,做一些记录 _declspec(naked)修饰可以生成一个“裸”函数, 使用后C编译器将生成不含函数框架的纯汇编代码,裸函数中什么都没有,所以也不能使用局部变量,只能全部用内嵌汇编实现。 可…

使用Presto、Trino数据库时提示“The datetime zone id ‘GMT+08:00‘ is not recognised”

出现这个问题的原因是:Presto、Trino的驱动使用了joda这个库来处理时区的问题。但这个库的编写人似乎对java zone的格式没有太多经验。先看一下出错的代码: com.facebook.presto.jdbc.internal.joda.time.DateTimeZone#forID 根据String类型的zoneId转成…

java日期常用操作

Testpublic void validateDateUtils(){// 1 字符串转换日期Date result DateUtil.parse("2023-08-01", com.alibaba.excel.util.DateUtils.DATE_FORMAT_10);log.info("result : [{}]" , result);// 2 日期转换字符串final Date date new Date();String f…

网络防御之SSL VPN

1. SSL工作过程是什么? 第一阶段: 客户端发送client hello消息到服务端,服务端收到client hello消息后,再发送server hello消息到客户端。 第二阶段: 服务器的证书,用于客户端给客户端发送信息时加密 serv…

韩顺平Linux基础篇

一、课程内容 二、Linux应用领域 一、Linux使用在哪些地方 Linux最强应用:服务器 三、Linux概述 三、Linux和Unix的关系 五、VM和Linux的安装 基本说明 学习Linux需要一个环境,我们需要创建一个虚拟机,然后再虚拟机上安装一个Centos系统来学…

将 Kwargs 传递给 Python 中的另一个函数

文章目录 Python 中的关键字参数在 Python 中使用**kwargs 调用函数使用 Python 将 kwargs 传递给另一个函数总结 Python 列出了可以传递给程序中的函数的两种类型的参数。 非关键字参数 (**args) 和关键字参数 (**kwargs)。 通常,python 函数必须使用正确数量的参…

春秋云镜 CVE-2022-24124

春秋云镜 CVE-2022-24124 Casdoor api 获取组织 SQL注入 靶标介绍 Casdoor是开源的一个身份和访问管理(IAM)/单点登录(SSO)平台,标记支持OAuth 2.0 / OIDC和SAML身份验证的Web UI。 Casdoor 1.13.1之前存在安全漏洞&…

Unity导入图片时,通过设置属性快速实现资源的压缩

是在学习tilemap绘制世界地图的时候发现的这个功能。 之前一直只是粗略的知道这部分是对应图片资源的压缩的。比如Compression是指的压缩质量,想要完全不压缩就设置None,会导致图片资源会大一些。 在我的例子工程中,其他图片资源的尺寸都是6…

服务器数据恢复-RAID5上层Hyper-V虚拟机数据恢复案例

服务器数据恢复环境: 一台Windows Server服务器,部署Hyper-V虚拟化环境,虚拟机的硬盘文件和配置文件存放在一台DELL存储中。该存储中有一组由4块硬盘组建的RAID5阵列,用来存放虚拟机的数据文件,另外还有一块大容量硬盘…

Centos7.6 安装mysql过程全记录

在centos 7.6上 离线安装mysql 的步骤,可参考下文: 一、查看当前MySQL的安装情况并卸载 1. 查看当前MySQL的安装情况 查找之前是否安装了MySQL rpm -qa|grep -i mysql 2.卸载mysql 如果已经安装mysql,则需要先停止MySQL,再删除…

基于springboot+vue的幼儿园管理系统的设计与实现_5umt6

随着世界经济信息化、全球网络化的到来推动信息线上管理的飞速发展,为幼儿园行业的改革起到关键作用。若想达到安全,快捷的目的,就需要拥有信息化的组织和管理模式,建立一套合理、畅通、高效的幼儿园管理系统。当前的幼儿园管理系…

【JAVA】继承

作者主页:paper jie的博客 本文作者:大家好,我是paper jie,感谢你阅读本文,欢迎一建三连哦。 本文录入于《JAVASE语法系列》专栏,本专栏是针对于大学生,编程小白精心打造的。笔者用重金(时间和精…

vue3官网文档学习、复习笔记(快速上手)

目录 2.Attribute 绑定(v-bind) 3.事件监听(v-on) 4.表单绑定(v-model) 5.条件渲染(v-if) 6.列表渲染(v-for) all.value all.value.filter(…

[C++]类与对象(下) -- 初始化列表 -- static成员 -- 友元 -- 内部类,一篇带你深度了解。

目录 1、再谈构造函数 1.1 构造函数体赋值 1.2 初始化列表 1.2.1 初始化列表的意义 1.3 explicit关键字 2、static成员 2.1 问题引入 2.2 特性 3、友元 3.1 友元函数 3.2 友元类 4、内部类 1、再谈构造函数 1.1 构造函数体赋值 在创建对象时,编译器通…

改进的麻雀算法优化最大相关峭度解卷积(SCSSA-MCKD),实现早期微弱故障诊断,MATLAB代码实现

01 引言 由于一些设备的早期故障产生的冲击十分微弱,易被系统噪声干扰,如何有效地对设备的原始故障信号进行降噪并增强信号中微弱冲击成分,是进行该类部件早期故障诊断的关键。 最大相关峭度解卷积(MCKD)通过解卷积运算…

干翻Dubbo系列第九篇:Dubbo体系中序列化详解

文章目录 文章说明 一:序列化概念 1:概念 2:Dubbo中序列化方式 二:Kyro序列化方案 1:引入依赖 2:XML的配置方式 3:Boot的方式 4:Consumer端调用 三:FST序列化方…