PO模式在Selenium中简单实践
初识PO模式
PO(PageObject)是一种设计模式。简单来说就是把一些繁琐的定位方法、元素操作方式等封装到类中,通过类与类之间的调用完成特定操作。
PO被认为是自动化测试项目开发实践的最佳设计模式之一。
在学习PO模式前,可以先复习一下面向对象的编程思想。我觉得两者很像。
优点
PO模式把页面元素定位和业务操作流程分开,界面元素的变化则不需要修改业务逻辑代码
PO能提高代码的可读性,高复用性,可维护性
设计准则
1.使用公共方法来代表页面提供的服务
2.不要暴露页面的内部细节(比如元素、元素的定位方法等),隔离测试用例和业务和页面对象
3.PO本身通常不应进行断言或判断。判断和断言是测试的一部分,而不是在PO中。
4.PO不一定需要代表整个界面,而是在测试中‘用到什么写什么’
5.相同的操作,但是数据不同,带来的不同结果可以封装成不同的方法。
6.方法可以返回其他的页面对象,进行页面的关联。
以上是比较官方的PO设计准则,我们需要根据具体业务的实际情况决定是完全遵循还是部分遵循。
selenium中的分层模型
表现层:页面中可见的元素,都属于表现层。(元素定位器的编写)
操作层:对页面可见元素的操作。(点击、输入文本等)
业务层:上面2层的组合,联合到一起形成某个业务动作。
测试用例:组合了一个或多个页面的方法,操作对应的元素,完成的测试。
PO模式实战
接下来就用PO模式完成一个简单的‘百度登录模块’的测试
思路:
1.创建一个elements.py存放登录界面所有的元素定位方式(用到哪个写哪个)
2.创建一个common_driver.py存放一些共用的浏览器相关方法
3.创建一个common_basepage.py存放共用的元素操作方法
4.创建一个test_cases.py文件存放测试用例
以下为部分代码:
找到我们测试登录模块需要操作到的元素,将其定位方法写到elements.py中
#elements.pyclass Elements():'''存放用到的所有元素定位器'''#登录前的界面元素LOGIN_BUTTON_OUT = ('id','s-top-loginbtn')#百度首页的‘登录’按钮LOGIN_WIN = ('id','TANGRAM__PSP_4__content')#登录窗口USERNAME_INPUT = ('id','TANGRAM__PSP_11__userName')#输入账号栏PASSWORD_INPUT = ('id','TANGRAM__PSP_11__password')#输入密码栏LOGIN_BUTTON_IN = ('id','TANGRAM__PSP_11__submit')#登录界面的‘登录’按钮#登录后的界面元素USER_INFO = ('css selector','#s-top-username > span.user-name.c-font-normal.c-color-t')#右上角的用户信息QUIT_BOTTON = ('css selector','#s-user-name-menu>.quit')#退出登录按钮
浏览器相关操作放到common_driver.py中
# common_driver.pyfrom selenium import webdriver
from environment_config import Envclass Single(object):'''设计单例模式'''_instance = None #实例def __new__(cls, *args, **kwargs):if cls._instance is None: #此处是可以用__instancecls._instance = super().__new__(cls)return cls._instance
class Open_Driver(Single):'''打开一个浏览器'''driver = Nonedef get_driver(self,browser_type=Env.BROWSER_TYPE,headless_flag=Env.HEADLESS_FLAG):'''根据参数打开想要的浏览器:param browser_type: 浏览器类型,读取Env文件中的值作为默认值:param headless_flag: 是否有头,读取Env文件中的值作为默认值,True/False:return: 返回一个浏览器对象'''if self.driver is None:if not headless_flag:#如果是有头模式if browser_type == 'chrome':self.driver = webdriver.Chrome()elif headless_flag == 'firefox':self.driver = webdriver.Firefox()else:raise Exception(f'暂不支持{browser_type}浏览器')else:#如果是无头模式_option = webdriver.ChromeOptions()_option.add_argument('--headless')#添加无头模式参数'--headless'if browser_type == 'chrome':self.driver = webdriver.Chrome(options=_option)elif headless_flag == 'firefox':self.driver = webdriver.Firefox(options=_option)else:raise Exception(f'暂不支持{browser_type}浏览器')self.driver.maximize_window()#窗口最大化self.driver.implicitly_wait(Env.IMPLICITLY_WAIT_TIME)#隐式等待,读取Env文件中IMPLICITLY_WAIT_TIME的值return self.driver #返回浏览器对象
把要用到的元素操作方法写入到common_basepage.py中
# common_basepage.py
from common_driver import Open_Driverclass BasePage():'''存放所有界面元素操作方法'''def __init__(self):self.driver = Open_Driver().get_driver()def open_url(self,url):'''打开网址'''self.driver.get(url)def get_element(self,locator):'''定位元素:param locator:元素定位器,从elements中取:return: 元素对象'''return self.driver.find_element(*locator)def input_text(self,locator,text,append=False):'''在元素上输入文本:param locator: 元素定位器:param text: 要输入的文本:param append: 是否先清空,默认清空'''if append:#不需要清空内容,追加写入self.driver.find_element(*locator).send_keys(text)else:#先清空,再写入self.driver.find_element(*locator).clear()self.driver.find_element(*locator).send_keys(text)def click_element(self,locator):'''点击元素:param locator: 元素定位器'''self.driver.find_element(*locator).click()def ele_find_ele_input(self,locator1,locator2,text):'''在元素1上找元素2:param ele1: 元素1:param ele2: 元素2:return: 元素2'''return self.driver.find_element(*locator1).find_element(*locator2).send_keys(text)def get_element_text(self,locator):return self.driver.find_element(*locator).text
页面对象loginpage.py
from common_basepage import BasePage
from datas import Datas
from elements import Elements
from logsuccesspage import LogSuccessPageclass LoginPage(BasePage):def open_loginpage(self,url):'''打开登录页:param url:登录页url:return: LoginPage实例对象'''self.open_url(url)return selfdef login_baidu(self,username,password):'''登录百度账号:param username: 用户名:param password: 密码:return: 登录成功后的页面对象'''self.click_element(Elements.LOGIN_BUTTON_OUT)#点击右上角登录self.ele_find_ele_input(Elements.LOGIN_WIN,Elements.USERNAME_INPUT,Datas.USERNAME)#输入账号self.ele_find_ele_input(Elements.LOGIN_WIN,Elements.PASSWORD_INPUT,Datas.PASSWORD)#输入密码self.click_element(Elements.LOGIN_BUTTON_IN)return LogSuccessPage()
测试用例test_cases.py
from time import sleep
import pytest
from datas import Datas
from elements import Elements
from environment_config import Env
from loginpage import LoginPageclass Test_login():def test_login01(self):'''登录成功的测试:return:'''test_page = LoginPage()#创建实例test_page.open_loginpage(Env.TEST_URL)#打开测试urlnew_page=test_page.login_baidu(Datas.USERNAME,Datas.PASSWORD)#登录百度账号sleep(2)text = new_page.get_element_text(Elements.USER_INFO)#登录成功界面assert text == 'yvvgfffvbh'#断言用户名称是否正确if __name__ == '__main__':pytest.main(['-vs'])
运行结果

写完花了4个小时,感受就是:
1.要理解透彻Python中的面向对象思想。
2.写完整体结构后要继续优化。
可以看到,我们所有数据都放在配置文件中,代码中不会暴露任何的界面元素或账号数据。 最后用pytest执行测试用例即可。
以上只是一个最简版的PO模型项目。只是遵循了po设计准则,并不完整。
一个完整的selenium测试项目大体上应该包括:
1.tools 工具类,格式转换、路径操作等
2.commom 基类,一些公用的方法
3.pageobjects 页面对象类
4.testcases 测试用例
5.test_datas 测试数据,yaml/Excel文件等
6.outfiles 输出文件,log和截图等
7.testreport 测试报告
项目结构并没有具体标准,分类清晰即可。重要的是在设计过程中遵循上文说到的’设计准则‘。
学习安排上
如果你不想再体验一次学习时找不到资料,没人解答问题,坚持几天便放弃的感受的话,在这里我给大家分享一些自动化测试的学习资源,希望能给你前进的路上带来帮助。

视频文档获取方式:
这份文档和视频资料,对于想从事【软件测试】的朋友来说应该是最全面最完整的备战仓库,这个仓库也陪伴我走过了最艰难的路程,希望也能帮助到你!以上均可以分享,点下方小卡片进群即可自行领取。
相关文章:
PO模式在Selenium中简单实践
初识PO模式 PO(PageObject)是一种设计模式。简单来说就是把一些繁琐的定位方法、元素操作方式等封装到类中,通过类与类之间的调用完成特定操作。 PO被认为是自动化测试项目开发实践的最佳设计模式之一。 在学习PO模式前,可以先…...
KubeSphere
文章目录一、概述二、最小化安装 KubeSphere2.1 前提2.2 安装 nfs 服务器一、概述 KubeSphere是在Kubernetes之上构建的以应用为中心的企业级分布式容器平台,提供简单易用的操作界面以及向导式操作方式,在降低用户使用容器调度平台学习成本的同时&#…...
JAVA基础阶段面试题(关键点)必备
1、简述什么是 JDK、JRE 和 JVM? JDK : 开发工具包JRE : 运行时环境JVM : java虚拟机2、写出Java的四类八种基本数据类?整数 byte short int long小数(浮点) float double布尔 boolean字符 char3、& 和 && 的区别 ?& 符号的左右两边,无…...
Shiro简介
介绍 ApacheShiro 是一个功能强大且易于使用的 Java 安全(权限)框架。Shiro 可以完成:认证、授权、加密、会话管理、与 Web集成、缓存等。借助Shiro 您可以快速轻松地保护任何应用程序一一从最小的移动应用程序到最大的 Web 和企业应用程序。 1.2:为什么要用 shiro 自2003年以…...
cmu 445 poject 3笔记
2022年的任务 https://15445.courses.cs.cmu.edu/fall2022/project3/ task1, 从磁盘读取数据的算子 task2, 聚合和join算子 task3, sort,limit,topn算子,以及sortlimit->TopN优化 leaderboard没做 本文不写代码,只记录遇到的一些思维盲点 Task1 scan…...
CHAPTER 2 Zabbix界面操作
Zabbix界面操作2.1 Zabbix界面操作1.zabbix的web界面安装2.添加监控信息3.查看监控内容4.查看图像2.2 自定义监控与监控报警1.自定义监控1.1 说明1.2 预备知识2.实现自定义监控2.1 自定义语法2.2 agent注册2.3 在server端注册(web操作)2.4 查看监控图形2.3 监控报警1.第三方报警…...
keep-alive的使用-及遇到的问题
被keep-alive包括的的组件,当组件切换是不是走销毁流程,而是缓存起来 keep-alive有三个参数include匹配name名被缓存,exclude匹配name名不会被缓存,max被缓存组件数量 不写,组件默认全部缓存 <keep-alive ><…...
华为OD面试经验分享,尤其注意机试题部分
文章目录招聘流程和背景介绍面试准备机试题目类型和解答技巧在算法部分在操作系统部分面试官提问和答题技巧面试总结和建议推荐一些华为 od 常见的机试题题目:两数之和题目:二叉树的遍历题目:链表反转题目:最大子序和招聘流程和背…...
【Java】String、StringBuffer、StringBuilder的区别
一、String 由 char[] 数组构成,使用了 final 修饰,String的值是不可变的,这就导致每次对String的操作都会生成新的String对象,然后把指针指向新的引用对象,不仅效率低下,而且浪费大量优先的内存空间。 二…...
iOS开发:对Block使用的一次研究总结
在开发中Block是经常使用的,那我们就得知其然,知其所以然。 Block是什么? Block可以封装一个匿名函数为对象,并捕获上下文所需的数据,并传给目标对象在适当的时候回调。我们使用Block的目的其实就是回调传值,那我们去看看Block的底层,再深入了解一下Block。 Block的底…...
Spark 3.1.1 shuffle fetch 导致shuffle错位的问题
背景 最近从数据仓库小组那边反馈了一个问题,一个SQL任务出来的结果不正确,重新运行一次之后就没问题了,具体的SQL如下: select col1,count(1) as cnt from table1 where dt 20230202 group by col1 having count(1) > 1这个问题是偶发…...
2月第2周榜单丨飞瓜数据B站UP主排行榜(哔哩哔哩平台)发布!
飞瓜轻数发布2023年2月6日-2月12日飞瓜数据UP主排行榜(B站平台),通过充电数、涨粉数、成长指数三个维度来体现UP主账号成长的情况,为用户提供B站号综合价值的数据参考,根据UP主成长情况用户能够快速找到运营能力强的B站…...
Jdk19 动态编译 Java源码为 Class 文件
动态编译 Java 源码为 Class一.背景1.Jdk 版本2.需求二.Java 源码动态编译实现1.Maven 依赖2.源码包装类3.Java 文件对象封装类4.文件管理器封装类5.类加载器6.类编译器三.动态编译测试1.普通测试类2.接口实现类3.测试四.用动态编译 Class 替换 SpringBoot 的 Bean(…...
安装 GPU 版本的 tensorflow 完整版本
前言: 之前安装的 CPU 版本的 tensorflow 一直出问题,索性就直接安装 GPU 版本的 tensorflow 了(有了GPU 就不能浪费)。 安装过程: 1)看自己有无 GPU,找到对应 GPU 的版本:任务管理…...
BOM编程-设置地址栏上的URL
<!DOCTYPE html> <html> <head> <meta charset"utf-8"> <title>设置地址栏上的URL</title> </head> <body> <script> function go(){ // 获…...
设计模式之原型模式与建造者模式详解和应用
目录1 原型模式1.1 原型模式定义1.2 原型模式的应用场景1.3 原型模式的通用写法(浅拷贝)1.4 使用序列化实现深度克隆1.5 克隆破坏单例模式1.6 原型模式在源码中的应用1.7 原型模式的优缺点1.8 总结2 建造者模式2.1 建造者模式定义2.2 建造者模式的应用场…...
C语言(函数和递归)
函数是完成特定任务的独立程序代码单元。 目录 一.函数 1.创建一个简单的函数 2.定义带形式参数的函数 3.使用return从函数中返回值 二.递归 一.函数 1.创建一个简单的函数 #include <stdio.h> void print(void); //函数原型 int main(){ print(); //函…...
快乐的shell命令行
快乐的shell命令行 PART1——基础 1.权限 #超级用户权限$普通用户 2.复制粘贴 复制:鼠标左键沿着文本拖动高亮的文本被复制到X管理的缓冲区(或者双击一个单词)粘贴:鼠标中键 3.简单命令 时间和日期date当前月份的日历cal磁…...
大数据面试题flume篇
1.Flume 的Source,Sink,Channel 的作用?你们Source 是什么类型? 1. 作用 (1)Source组件是专门用来收集数据的,可以处理各种类型、各种格式的日志数据,包括 avro、thrift、exec、jm…...
零信任-深信服零信任aTrust介绍(5)
深信服零信任aTrust介绍 深信服是国内领先的互联网信任服务提供商,也是国内首家通过认证的全球信任服务商。深信服零信任是其中一项核心的信任技术,主要针对身份认证、数字签名、数字证书等方面的信任问题。 深信服零信任提供了一种新的安全保护模式…...
SciencePlots——绘制论文中的图片
文章目录 安装一、风格二、1 资源 安装 # 安装最新版 pip install githttps://github.com/garrettj403/SciencePlots.git# 安装稳定版 pip install SciencePlots一、风格 简单好用的深度学习论文绘图专用工具包–Science Plot 二、 1 资源 论文绘图神器来了:一行…...
在鸿蒙HarmonyOS 5中实现抖音风格的点赞功能
下面我将详细介绍如何使用HarmonyOS SDK在HarmonyOS 5中实现类似抖音的点赞功能,包括动画效果、数据同步和交互优化。 1. 基础点赞功能实现 1.1 创建数据模型 // VideoModel.ets export class VideoModel {id: string "";title: string ""…...
遍历 Map 类型集合的方法汇总
1 方法一 先用方法 keySet() 获取集合中的所有键。再通过 gey(key) 方法用对应键获取值 import java.util.HashMap; import java.util.Set;public class Test {public static void main(String[] args) {HashMap hashMap new HashMap();hashMap.put("语文",99);has…...
《Playwright:微软的自动化测试工具详解》
Playwright 简介:声明内容来自网络,将内容拼接整理出来的文档 Playwright 是微软开发的自动化测试工具,支持 Chrome、Firefox、Safari 等主流浏览器,提供多语言 API(Python、JavaScript、Java、.NET)。它的特点包括&a…...
从零开始打造 OpenSTLinux 6.6 Yocto 系统(基于STM32CubeMX)(九)
设备树移植 和uboot设备树修改的内容同步到kernel将设备树stm32mp157d-stm32mp157daa1-mx.dts复制到内核源码目录下 源码修改及编译 修改arch/arm/boot/dts/st/Makefile,新增设备树编译 stm32mp157f-ev1-m4-examples.dtb \stm32mp157d-stm32mp157daa1-mx.dtb修改…...
ios苹果系统,js 滑动屏幕、锚定无效
现象:window.addEventListener监听touch无效,划不动屏幕,但是代码逻辑都有执行到。 scrollIntoView也无效。 原因:这是因为 iOS 的触摸事件处理机制和 touch-action: none 的设置有关。ios有太多得交互动作,从而会影响…...
精益数据分析(97/126):邮件营销与用户参与度的关键指标优化指南
精益数据分析(97/126):邮件营销与用户参与度的关键指标优化指南 在数字化营销时代,邮件列表效度、用户参与度和网站性能等指标往往决定着创业公司的增长成败。今天,我们将深入解析邮件打开率、网站可用性、页面参与时…...
HDFS分布式存储 zookeeper
hadoop介绍 狭义上hadoop是指apache的一款开源软件 用java语言实现开源框架,允许使用简单的变成模型跨计算机对大型集群进行分布式处理(1.海量的数据存储 2.海量数据的计算)Hadoop核心组件 hdfs(分布式文件存储系统)&a…...
Aspose.PDF 限制绕过方案:Java 字节码技术实战分享(仅供学习)
Aspose.PDF 限制绕过方案:Java 字节码技术实战分享(仅供学习) 一、Aspose.PDF 简介二、说明(⚠️仅供学习与研究使用)三、技术流程总览四、准备工作1. 下载 Jar 包2. Maven 项目依赖配置 五、字节码修改实现代码&#…...
Mysql中select查询语句的执行过程
目录 1、介绍 1.1、组件介绍 1.2、Sql执行顺序 2、执行流程 2.1. 连接与认证 2.2. 查询缓存 2.3. 语法解析(Parser) 2.4、执行sql 1. 预处理(Preprocessor) 2. 查询优化器(Optimizer) 3. 执行器…...
