当前位置: 首页 > news >正文

初识PO模式并在Selenium中简单实践

初识PO模式

PO(PageObject)是一种设计模式。简单来说就是把一些繁琐的定位方法、元素操作方式等封装到类中,通过类与类之间的调用完成特定操作。

PO被认为是自动化测试项目开发实践的最佳设计模式之一。

在学习PO模式前,可以先复习一下面向对象的编程思想。我觉得两者很像。

优点

  • PO模式把页面元素定位和业务操作流程分开,界面元素的变化则不需要修改业务逻辑代码
  • PO能提高代码的可读性,高复用性,可维护性

设计准则

1.使用公共方法来代表页面提供的服务

2.不要暴露页面的内部细节(比如元素、元素的定位方法等),隔离测试用例和业务和页面对象

3.PO本身通常不应进行断言或判断。判断和断言是测试的一部分,而不是在PO中。

4.PO不一定需要代表整个界面,而是在测试中‘用到什么写什么’

5.相同的操作,但是数据不同,带来的不同结果可以封装成不同的方法。

6.方法可以返回其他的页面对象,进行页面的关联。

以上是比较官方的PO设计准则,我们需要根据具体业务的实际情况决定是完全遵循还是部分遵循。

​现在我也找了很多测试的朋友,做了一个分享技术的交流群,共享了很多我们收集的技术文档和视频教程。
如果你不想再体验自学时找不到资源,没人解答问题,坚持几天便放弃的感受
可以加入我们一起交流。而且还有很多在自动化,性能,安全,测试开发等等方面有一定建树的技术大牛
分享他们的经验,还会分享很多直播讲座和技术沙龙
可以免费学习!划重点!开源的!!!
qq群号:485187702【暗号:csdn11】

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 测试报告

项目结构并没有具体标准,分类清晰即可。重要的是在设计过程中遵循上文说到的’设计准则‘。

 

最后感谢每一个认真阅读我文章的人,看着粉丝一路的上涨和关注,礼尚往来总是要有的,虽然不是什么很值钱的东西,如果你用得到的话可以直接拿走! 希望能帮助到你!【100%无套路免费领取】

相关文章:

初识PO模式并在Selenium中简单实践

初识PO模式 PO(PageObject)是一种设计模式。简单来说就是把一些繁琐的定位方法、元素操作方式等封装到类中,通过类与类之间的调用完成特定操作。 PO被认为是自动化测试项目开发实践的最佳设计模式之一。 在学习PO模式前,可以先…...

读书笔记:彼得·德鲁克《认识管理》第35章 以任务和工作为中心的设计

一、章节内容概述 如今我们已经拥有五种不同的组织设计原则,其中每一种都能够满足若干设计规范,但任何一种都不能满足所有规范;其中每一种都有自身的优势、劣势和对效果的严格要求,都蕴含着特定的组织设计逻辑。其中前两种设计原则&#xff…...

算法基础课 (一) 基础算法

进制转换 #include<iostream> using namespace std; const int N 100; int n,m; string s; int x;//记录n进制转化成十进制; int ans[N]; int main(){cin>>n>>s>>m;int t1;for(int is.size()-1;i>0;i--){if(s[i]<A){x t*(int)(s[i]-0);t * n;…...

【Python】jieba分词基础

jieba分词主要有3种模式&#xff1a; 1、精确模式&#xff1a;jieba.cut(文本, cut_allFalse) 2、全模式&#xff1a;jieba.cut(文本, cut_allTrue) 3、搜索引擎模式&#xff1a;jieba.cut_for_search(文本) 分词后的关键词提取&#xff1a; jieba.analyse.textrank(txt,t…...

使用jmeter对接口进行简单测试

JMeter是一个开源的性能测试工具&#xff0c;它可以对于Web应用程序、FTP、数据库服务器等各种服务器进行性能测试和负载测试&#xff0c;以确定它们是否能够承受预期的负载。JMeter支持多种协议和技术&#xff0c;如HTTP、HTTPS、FTP、JDBC、LDAP、SOAP、JMS等。它使用Java编写…...

成长在于积累——https 认证失败的学习与思考

1. 引言 本周二长城项目在收尾过程中&#xff0c;出现了一个车端无法进行注册的问题&#xff1a;curl提示证书认证失败&#xff08;其实已经能确认问题方向了&#xff0c;运维人员去确认证书问题即可&#xff09;。虽然最终的原因是由于长城运维人员导致的。但是这个过程让我颇…...

C语言——数字金字塔

实现函数输出n行数字金字塔 #define _CRT_SECURE_NO_WARNINGS 1#include <stdio.h>void pyramid(int n) {int i,j,k;for (i1; i<n; i){//输出左边空格&#xff0c;空格数为n-i for (j1; j<n-i; j){printf(" "); } //每一行左边空格输完后输出数字&#…...

关于 typedef 的用法

typedef 是 C 和 C 语言中的关键字&#xff0c;用于创建类型的别名。它的主要作用是给一个已有的类型定义一个新的名称&#xff0c;以提高代码的可读性和可维护性。下面是 typedef 的几种常见用法&#xff1a; 用于给基本类型定义别名&#xff1a; typedef int myint;上述代码…...

Webshell流量分析

Webshell流量分析 常见的一句话木马: asp一句话 <%eval request("pass")%> aspx一句话 <%@ Page Language="Jscript"%><%eval(Request.Item["pass"],"unsafe");%> php一句话 <?php @eval($_POST["pass&…...

高级IO—poll,epoll,reactor

高级IO—poll,epoll,reactor 文章目录 高级IO—poll,epoll,reactorpoll函数poll函数接口poll服务器 epollepoll的系统调用epoll_createepoll_ctlepoll_wait epoll的工作原理epoll的工作方式水平触发边缘触发 epoll服务器 reactor poll函数 poll函数是一个用于多路复用的系统调…...

一文详解Python中常用数据类型

文章目录 Python 中常用的数据类型包括&#xff1a;Python 中布尔类型(bool)Python 中的数字类型概述Pyhon中的字符串概述Python 中的List概述Python 中的元组类型(tuple)Python中的字典&#xff08;Dictionary&#xff09;Python中的集合&#xff08;Set&#xff09;Python中的…...

【MATLAB源码-第85期】基于farrow结构的滤波器仿真,截止频率等参数可调。

操作环境&#xff1a; MATLAB 2022a 1、算法描述 Farrow结构是一种用于实现可变数字滤波器的方法&#xff0c;尤其适用于数字信号处理中的采样率转换和时变滤波。它通过多项式近似来实现对滤波器系数的平滑变化&#xff0c;使得滤波器具有可变的群延时或其他参数。 Farrow结…...

ChatGPT Plus/GPT4高级数据分析和插件功能详解

ChatGPT 在论文写作与编程方面也具备强大的能力。无论是进行代码生成、错误调试还是解决编程难题&#xff0c;ChatGPT都能为您提供实用且高质量的建议和指导&#xff0c;提高编程效率和准确性。此外&#xff0c;ChatGPT是一位出色的合作伙伴&#xff0c;可以为您提供论文写作的…...

【Android Jetpack】Room数据库

文章目录 引入EntitiesPrimary Key主键索引和唯一性对象之间的关系外键获取关联的Entity对象嵌套对象Data Access Objects&#xff08;DAOs&#xff09;使用Query注解的方法简单的查询带参数查询返回列的子集可被观察的查询 数据库迁移用法 引入 原始的SQLite有以下两个缺点: …...

自定义中间件

1.使用 app.use0来定义全局生效的中间件 // 导入 express 模块 const express require(express) // 创建 express的服务器实例 const app express() app.use(function(req, res, next) {// 中间件的业务逻辑 }) 2.监听 req 的 data 事件 在中间件中&#xff0c;需要监听 re…...

优化机器学习:解析数据归一化的重要性与应用

在机器学习中&#xff0c;数据归一化是一种数据预处理的技术&#xff0c;旨在将数据转换为相似的范围或标准化的分布。这样做的主要目的是消除不同特征之间的量纲差异或数值范围差异&#xff0c;以确保模型在训练时更稳定、更有效地学习特征之间的关系。 通常&#xff0c;机器…...

五分钟,Docker安装flink,并使用flinksql消费kafka数据

1、拉取flink镜像&#xff0c;创建网络 docker pull flink docker network create flink-network2、创建 jobmanager # 创建 JobManager docker run \-itd \--namejobmanager \--publish 8081:8081 \--network flink-network \--env FLINK_PROPERTIES"jobmanager.rpc.ad…...

【小聆送书第一期】让架构师的成神之路温暖你这个不景气的冬天

&#x1f308;个人主页&#xff1a;聆风吟 &#x1f525;系列专栏&#xff1a;网络奇遇记、数据结构 &#x1f516;少年有梦不应止于心动&#xff0c;更要付诸行动。 文章目录 &#x1f4cb;前言 书籍一览 ⛳️书籍一⛳️书籍二⛳️书籍三⛳️书籍四⛳️书籍五⛳️书籍六⛳️书…...

网页爬虫反扒措施有哪些?

爬虫之常见的反扒 cookies 一般用requests直接请求网址的时候有时候可能会遇到反扒措施&#xff0c;这时候可以考虑一下加上user-agent伪装成浏览器&#xff1b;也可能有登录限制&#xff0c;这时候cookies就有用处了 浏览器中的cookie是保存我们的账号数据和访问记录&#…...

C#实现批量生成二维码

相信大家都使用过草料二维码生成器&#xff0c;单独生成二维码可以&#xff0c;但是批量生成二维码就需要收费了。既然要收费&#xff0c;那就自己写一个。 接口采用导入Excel文件生成二维码&#xff0c;首先需要读取Excel的数据&#xff0c;方法如下所示&#xff1a; /// <…...

pam_env.so模块配置解析

在PAM&#xff08;Pluggable Authentication Modules&#xff09;配置中&#xff0c; /etc/pam.d/su 文件相关配置含义如下&#xff1a; 配置解析 auth required pam_env.so1. 字段分解 字段值说明模块类型auth认证类模块&#xff0c;负责验证用户身份&am…...

【解密LSTM、GRU如何解决传统RNN梯度消失问题】

解密LSTM与GRU&#xff1a;如何让RNN变得更聪明&#xff1f; 在深度学习的世界里&#xff0c;循环神经网络&#xff08;RNN&#xff09;以其卓越的序列数据处理能力广泛应用于自然语言处理、时间序列预测等领域。然而&#xff0c;传统RNN存在的一个严重问题——梯度消失&#…...

对WWDC 2025 Keynote 内容的预测

借助我们以往对苹果公司发展路径的深入研究经验&#xff0c;以及大语言模型的分析能力&#xff0c;我们系统梳理了多年来苹果 WWDC 主题演讲的规律。在 WWDC 2025 即将揭幕之际&#xff0c;我们让 ChatGPT 对今年的 Keynote 内容进行了一个初步预测&#xff0c;聊作存档。等到明…...

MODBUS TCP转CANopen 技术赋能高效协同作业

在现代工业自动化领域&#xff0c;MODBUS TCP和CANopen两种通讯协议因其稳定性和高效性被广泛应用于各种设备和系统中。而随着科技的不断进步&#xff0c;这两种通讯协议也正在被逐步融合&#xff0c;形成了一种新型的通讯方式——开疆智能MODBUS TCP转CANopen网关KJ-TCPC-CANP…...

Qt Http Server模块功能及架构

Qt Http Server 是 Qt 6.0 中引入的一个新模块&#xff0c;它提供了一个轻量级的 HTTP 服务器实现&#xff0c;主要用于构建基于 HTTP 的应用程序和服务。 功能介绍&#xff1a; 主要功能 HTTP服务器功能&#xff1a; 支持 HTTP/1.1 协议 简单的请求/响应处理模型 支持 GET…...

【学习笔记】深入理解Java虚拟机学习笔记——第4章 虚拟机性能监控,故障处理工具

第2章 虚拟机性能监控&#xff0c;故障处理工具 4.1 概述 略 4.2 基础故障处理工具 4.2.1 jps:虚拟机进程状况工具 命令&#xff1a;jps [options] [hostid] 功能&#xff1a;本地虚拟机进程显示进程ID&#xff08;与ps相同&#xff09;&#xff0c;可同时显示主类&#x…...

Android Bitmap治理全解析:从加载优化到泄漏防控的全生命周期管理

引言 Bitmap&#xff08;位图&#xff09;是Android应用内存占用的“头号杀手”。一张1080P&#xff08;1920x1080&#xff09;的图片以ARGB_8888格式加载时&#xff0c;内存占用高达8MB&#xff08;192010804字节&#xff09;。据统计&#xff0c;超过60%的应用OOM崩溃与Bitm…...

Java线上CPU飙高问题排查全指南

一、引言 在Java应用的线上运行环境中&#xff0c;CPU飙高是一个常见且棘手的性能问题。当系统出现CPU飙高时&#xff0c;通常会导致应用响应缓慢&#xff0c;甚至服务不可用&#xff0c;严重影响用户体验和业务运行。因此&#xff0c;掌握一套科学有效的CPU飙高问题排查方法&…...

让回归模型不再被异常值“带跑偏“,MSE和Cauchy损失函数在噪声数据环境下的实战对比

在机器学习的回归分析中&#xff0c;损失函数的选择对模型性能具有决定性影响。均方误差&#xff08;MSE&#xff09;作为经典的损失函数&#xff0c;在处理干净数据时表现优异&#xff0c;但在面对包含异常值的噪声数据时&#xff0c;其对大误差的二次惩罚机制往往导致模型参数…...

基于Java Swing的电子通讯录设计与实现:附系统托盘功能代码详解

JAVASQL电子通讯录带系统托盘 一、系统概述 本电子通讯录系统采用Java Swing开发桌面应用&#xff0c;结合SQLite数据库实现联系人管理功能&#xff0c;并集成系统托盘功能提升用户体验。系统支持联系人的增删改查、分组管理、搜索过滤等功能&#xff0c;同时可以最小化到系统…...