day16-测试自动化之selenium的PO模式
一、PO模式介绍
PO(Page Object)模式是一种在自动化测试中常用的设计模式,将页面的每个元素封装成一个对象,通过操作对象来进行页面的交互。
一般分为六个版本,现在大部分企业都用的V4版本,三层结构(base+page+scripts)
V1:不使用任何设计模式和单元测试框架
问题:无法批量运行
代码例子
# 导包
from time import sleep
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait# 获取浏览器对象
chromedriver_path = r"C:\Program Files\Google\Chrome\Application\chromedriver.exe"
service = Service(executable_path=chromedriver_path)
driver = webdriver.Chrome(service=service)def main():# 打开页面driver.get("http://www.tpshop.com/index.php")# 网页最大化driver.maximize_window()# 隐式等待driver.implicitly_wait(30)# 点击登录链接driver.find_element(By.CSS_SELECTOR,"body > div.tpshop-tm-hander > div.top-hander.clearfix > div > div > div.fl.nologin > a.red").click()# 输入用户名driver.find_element(By.CSS_SELECTOR,"#username").send_keys("admin")# 输入密码driver.find_element(By.CSS_SELECTOR,"#password").send_keys("123456")# 输入验证码driver.find_element(By.CSS_SELECTOR,"#verify_code").send_keys("8888")# 点击登录按钮driver.find_element(By.CSS_SELECTOR,"#loginform > div > div.login_bnt > a").click()# 获取错误提示信息msg = driver.find_element(By.CSS_SELECTOR,".layui-layer-content").text# 断言# assert msg == "用户名不存在!"# assertNotIn()# 点击提示框确定按钮driver.find_element(By.CSS_SELECTOR,".layui-layer-btn0").click()# 暂停两秒钟sleep(2)# 关闭浏览器驱动driver.quit()if __name__ == '__main__':main()
V2:使用UnitTeSt管理用例
问题:业务脚本没有与页面对象分开
V3:使用方法封装的思想,对代码进行优化
问题:代码冗余量太大了
V4:采用PO模式的分层思想对代码进行拆分
结构:
base(基类):page页面一些公共的方法;
1.初始化方法
2.查找元素方法
3.点击元素方法
4.输入方法
5.获取文本方法
6.截图方法
扩展:loc变量:类型为元组:*loc为解包
注意:
1.以上方法封装时候,解包只需1此,在查找元素解包
2.driver为虚拟,谁调用base时,谁传入,无需关注从哪里来
3.loc:真正使用1oc的方法只有查找元素方法使用
代码
import time
import page
from selenium.webdriver.support.wait import WebDriverWait
from base.get_logger import GetLogger# 获取log日志器
log = GetLogger().get_logger()class Base:def __init__(self, driver):log.info("[base]: 正在获取初始化driver对象:{}".format(driver))self.driver = driver# 查找元素方法 封装def base_find(self, loc, timeout=30, poll=0.5):log.info("[base]: 正在定位:{} 元素,默认定位超时时间为: {}".format(loc, timeout))# 使用显示等待 查找元素return WebDriverWait(self.driver,timeout=timeout,poll_frequency=poll).until(lambda x: x.find_element(*loc))# 点击元素 方法封装def base_click(self, loc):log.info("[base]: 正在对:{} 元素实行点击事件".format(loc))self.base_find(loc).click()# self.driver.execute_script("arguments[0].click();", self.base_find(loc))# 输入元素 方法封装def base_input(self, loc, value):# 获取元素el = self.base_find(loc)# 清空log.info("[base]: 正在对:{} 元素实行清空".format(loc))el.clear()# 输入el.send_keys(value)# 获取文本信息 方法封装def base_get_text(self, loc):log.info("[base]: 正在获取:{} 元素文本值".format(loc))return self.base_find(loc).text# 截图 方法封装def base_get_image(self):log.info("[base]: 断言出错,调用截图")self.driver.get_screenshot_as_file("./image/{}.png".format(time.strftime("%Y_%m_%d %H_%M_%S")))# 判断元素是否存在 方法封装def base_element_is_exist(self, loc):try:self.base_find(loc, timeout=5)log.info("[base]: {} 元素查找成功,存在页面".format(loc))return True # 代表元素存在except Exception as e:log.error("[base]:发生错误{},{} 元素查找失败,不存在当前页面".format(e, loc))return False # 代表元素不存在# 回到首(页购物车、下订单、支付)都需要用到此方法def base_index(self):time.sleep(5)self.driver.get(page.URL)# 切换frame表单方法def base_switch_frame(self, name):self.driver.switch_to.frame(name)# 回到默认目录方法def base_default_content(self):self.driver.switch_to.default_content()# 切换窗口 方法 调用此方法def base_switch_to_window(self, title):log.info("正在执行切换title值为:{}窗口 ".format(title))self.driver.switch_to.window(self.base_get_title_handle(title))# 获取指定title页面的handle方法def base_get_title_handle(self, title):# 获取当前页面所有的handlesfor handle in self.driver.window_handles:log.info("正在遍历handles:{}-->{}".format(handle, self.driver.window_handles))# 切换 handleself.driver.switch_to.window(handle)log.info("切换 :{} 窗口".format(handle))# 获取当前页面title 并判断 是否等于 指定参数titlelog.info("判断当前页面title:{} 是否等于指定的title:{}".format(self.driver.title, title))if self.driver.title == title:log.info("条件成立! 返回当前handle{}".format(handle))# 返回 handlereturn handle
page(页面对象):一个页面封装成一个对象;
应用:继承base;
实现:
1.模块名:page+实际操作模块名称 如:page_login.py
2.页面对象名:以大驼峰方法将模块名抄进来,有下划线去掉下划线
3.方法:涉及元素,将每个元素操作单独封装一个操作方法
4.组装:根据需求组装以上操作步骤
代码
from base.base import Base
import page
from base.get_logger import GetLogger# 获取log日志器
log = GetLogger().get_logger()class PageLogin(Base):# 点击 登录链接def page_click_login_link(self):log.info("[page_loging] 执行:{} 点击链接操作".format(page.login_link))self.base_click(page.login_link)# 输入用户名def page_input_username(self, username):log.info("[page_loging] 对:{} 元素 输入用户名:{} 操作".format(page.login_username, username))self.base_input(page.login_username, username)# 输入密码def page_input_pwd(self, pwd):log.info("[page_loging] 对:{} 元素 输入密码:{} 操作".format(page.login_pwd, pwd))self.base_input(page.login_pwd, pwd)# 输入验证码def page_input_verify_code(self, verify_code):log.info("[page_loging] 对:{} 元素 输入验证码:{} 操作".format(page.login_verify_code, verify_code))self.base_input(page.login_verify_code, verify_code)# 点击登录按钮def page_click_login_btn(self):self.base_click(page.login_btn)# 获取 错误提示信息def page_get_error_info(self):return self.base_get_text(page.login_err_info)# 点击 错误提示框 确定按钮def page_click_error_alert(self):self.base_click(page.login_err_ok_btn)# 判断是否登录成功def page_if_login_success(self):# 注意 一定要将找元素的结果返回,True:存在return self.base_element_is_exist(page.login_logout_link)# 点击 安全退出def page_click_logout_link(self):self.base_click(page.login_logout_link)# 判断是否退出成功def page_if_logout_success(self):return self.base_element_is_exist(page.login_link)# 组合业务方法 -->登录业务直接调用def page_login(self, username, pwd, verify_code):log.info("[page_loging] 正在执行登录操作, 用户名:{} 密码:{}, 验证码:{}".format(username, pwd, verify_code))# 调用 输入用户名self.page_input_username(username)# 调用 输入密码self.page_input_pwd(pwd)# 调用 输入验证码self.page_input_verify_code(verify_code)# 调用 点击登录self.page_click_login_btn()# 组合登录业务方法 给(购物车模块、订单模块、支付模块)依赖登录使用def page_login_success(self, username="13812345678", pwd="123456", verify_code="8888"):# 点击登录连接self.page_click_login_link()log.info("[page_loging] 正在执行登录操作, 用户名:{} 密码:{}, 验证码:{}".format(username, pwd, verify_code))# 调用 输入用户名self.page_input_username(username)# 调用 输入密码self.page_input_pwd(pwd)# 调用 输入验证码self.page_input_verify_code(verify_code)# 调用 点击登录self.page_click_login_btn()
sripts/cases(业务层):导包调用page页面
实现
1.模块:test+实际操作模块名称如:test_login.py
2.测试业务名称:以大驼峰方法将模块名抄进来,有下划线去掉下划线
3.方法
1).初始化方法setup()注:在unittest框架中不能使用def__init_()方法
1.1).实例化页面对象
1.2).前置操作如:打开等等
2).结束方法teardown
2.1).关闭驱动
3).测试方法
3.1).根据要操作的业务实现
代码
import unittestfrom base.get_driver import GetDriver
from page.page_login import PageLogin
from parameterized import parameterized
from tool.read_txt import read_txt
from base.get_logger import GetLogger# 获取log日志器
log = GetLogger().get_logger()def get_data():arrs = []for data in read_txt("login.txt"):arrs.append(tuple(data.strip().split(",")))return arrs[1:]# 新建 登录测试类 并 继承 unittest.TestCase
class TestLogin(unittest.TestCase):# 新建 setupClass@classmethoddef setUpClass(cls):try:# 实例化 并获取drivercls.driver = GetDriver().get_driver()# 实例化 PageLogin()cls.login = PageLogin(cls.driver)# 点击登录连接cls.login.page_click_login_link()except Exception as e:log.error("错误:{}".format(e))# 截图cls.login.base_get_image()# 新建 tearDownClass@classmethoddef tearDownClass(cls):# 关闭drier驱动对象GetDriver().quit_driver()# 新建 登录测试方法@parameterized.expand(get_data())def test_login(self, username, pwd, verify_code, expect_result, status):try:# 调用 登录业务方法self.login.page_login(username, pwd, verify_code)# 判断是否为正向if status == "true":# 断言是否登录成功try:self.assertTrue(self.login.page_if_login_success())except Exception as e:# 截图self.login.base_get_image()log.error("错误:{}".format(e))raise # 主动抛出异常,否则HTMLTestRunner无法显示该错误# 点击 安全退出self.login.page_click_logout_link()# 点击登录连接self.login.page_click_login_link()# 逆向用例else:# 获取错误提示信息msg = self.login.page_get_error_info()print("msg:", msg)try:self.assertEqual(msg, expect_result)except Exception as e:# 截图self.login.base_get_image()log.error("错误:{}".format(e))raise# 点击错误提示框 确定按钮self.login.page_click_error_alert()except Exception as e:log.error("错误:{}".format(e))# 截图self.login.base_get_image()raise
V5:对PO分层之后的代码继续优化
V6:PO模式深入封装,把共同操作提取封装到父类中,子类直接调用父类的方法
二、今日学习思维导图

相关文章:
day16-测试自动化之selenium的PO模式
一、PO模式介绍 PO(Page Object)模式是一种在自动化测试中常用的设计模式,将页面的每个元素封装成一个对象,通过操作对象来进行页面的交互。 一般分为六个版本,现在大部分企业都用的V4版本,三层结构…...
Springboot+freemarker大段文本内容动态修改输出,所见即所得
场景:给领导导出数据时,需要给出一个针对专业名词的解释说明,因此会存在有大批量的、大段的文本内容。如果直接写在代码里面,没啥大问题,但是大量的拼接替换、格式样式、后续修改维护等,都不是很方便。如果…...
Kali Linux网络问题解决与静态IP配置技巧
很多用户在使用 Kali Linux 时会遇到无法联网的问题,尤其是在 VMware 虚拟机中。这种情况相当常见,一般都是没有配置DNS服务器或者网卡配置文件的IP和虚拟网络编辑器的IP不一致所导致的,下面我们将探讨如何在 Kali Linux 中配置 DNS 服务和设…...
网络状态码-经验笔记
网络状态码-经验笔记 引言 在网络通信中,HTTP(Hypertext Transfer Protocol)状态码是服务器向客户端(通常是Web浏览器)发送响应时所包含的重要信息之一。 这些状态码指示了客户端请求的结果。 了解并正确使用这些状态…...
c++ 实现 actor 框架
服务端:https://github.com/xukeawsl/coro_actor 客户端:https://github.com/xukeawsl/coro_actor_client...
应对猫咪掉毛挑战,希喂、小米热门宠物空气净化器实测功效PK
随着养宠人群的增多,铲屎官们的需求日益增长,市场上出现了很多品牌的宠物空气净化器。然而,产品质量参差不齐,给消费者选择带来不少困难。劣质宠物空气净化器不仅无法有效去除宠物毛发、皮屑、异味及空气中的有害微粒,…...
0002 保险会计及其特殊性
保险会计是将会计理论专门应用于保险公司的专业会计领域,它是会计学的一个重要分支。作为一个分支,保险会计具有独特的特性,这些特性主要表现在以下几个方面: 产品的无形性:保险产品本质上是一种无形的商品,…...
ChatTTS:终极文本转语音工具,支持API!
ChatTTS:终极文本转语音工具,支持API! 文本转语音(TTS)系统的发展已经取得了长足的进步。从最初的机械化、平坦的声音,到如今听起来令人惊讶的人声,ChatTTS作为这一领域的新成员,旨…...
VUE和Element Plus
1.VUE 1.下载和配置环境 使用vue编程,我们需要使用到的编程软件是vs code,还需要使用node.js,这个的作用就类似于JDK,当我们都下载好之后,winR键打开命令提示符,我们在这里可以查看版本, npm…...
Python学习笔记(五)
""" 演示tuple元组的定义和操作 """# 元组一旦定义完成,就不可修改 # 定义元组 # t1 (1, "Hello", True) # t2 () # 定义空元组 # t3 tuple() #定义空元组 # print(f"t1的类型是:{type(t1)}, 内容是&…...
Linux企业级应用(一)构建企业级Linux应用平台:全面指南
文章目录 构建企业级Linux应用平台:全面指南前言1. Linux企业级应用简介2. 构建企业级网站应用平台使用LNMP架构构建Web服务器部署MySQL数据库主从复制与读写分离 3. 实施虚拟化技术部署KVM虚拟化平台使用LVS和Keepalived实现负载均衡与高可用性 4. 文件系统与分布式…...
LeetCode112 路径总和
前言 题目: 112. 路径总和 文档: 代码随想录——路径总和 编程语言: C 解题状态: 成功解答! 思路 比较简单的一个思路是遍历所有的路径,求和后再查找目标值。但是,最好的方法是一边遍历&#x…...
TI AWR1843 毫米波雷达实物展示
引言 随着自动驾驶、工业自动化以及智能交通系统的快速发展,雷达传感器在现代科技中的重要性日益提升。毫米波雷达凭借其高精度测距、抗干扰能力强等特点,逐渐成为各类感知系统中的关键技术。德州仪器(TI)推出的 AWR1843 毫米波雷…...
前端JS总结(下)之事件操作
目录 前言 事件基础 事件的三部分: 常见的事件: 鼠标事件: 键盘事件: 表单事件: onfocus和onblur:获取焦点和失去焦点 onselect:选中单行文本框/多行文本框中的内容 onchangeÿ…...
如何妙用哈希表来优化遍历查找过程?刷题感悟总结,c++实现
先上题目 题目链接:题目链接 这题我最先想到的就是前缀和a,构造好了以后就遍历每一个[l,r]数组(满足题目要求的连续区间数组),奈何倒数第二个样例时间超限 先给出原思路代码 class Solution { public:int subarray…...
【设计模式】漫谈设计模式
这篇文章里说一下对设计模式的个人的理解。本篇文章更类似于随笔而非技术文档。 设计模式最早是在上个世纪就被人提出来了,如今被奉为圣经,也就是GOF等人写的《设计模式》,其中的设计模式,是指导开发者如何进行开发出高内聚、低耦…...
第N5周:Pytorch文本分类入门
本文为365天深度学习训练营 中的学习记录博客原作者:K同学啊 任务: ●1. 了解文本分类的基本流程 ●2. 学习常用数据清洗方法 ●3. 学习如何使用jieba实现英文分词 ●4. 学习如何构建文本向量 一、前期准备 环境安装 这是一个使用PyTorch实现的简单文…...
SpringBoot 自定义 starter
1. 官方文档 SpringBoot 版本 2.6.13,相关链接 Developing with Spring Boot 1.1 什么是 Starter Starters are a set of convenient dependency descriptors that you can include in your application. You get a one-stop shop for all the Spring and relate…...
TDengine Invalid data format 问题定位
Invalid data format 看语义是数据类型不符,通常这个报错出现在使用行协议写入时。 如果是批量数据写入,想定位是哪条语句的问题,需要查看客户端日志。 如何确定使用的是哪个日志 lsof -p pidof taosadapter | grep taoslog如果没有安装lso…...
Spring Boot 使用 MongoDB 教程
🍁 作者:知识浅谈,CSDN签约讲师,CSDN博客专家,华为云云享专家,阿里云专家博主 📌 擅长领域:全栈工程师、爬虫、ACM算法 🔥 微信:zsqtcyw 联系我领取学习资料 …...
多模态2025:技术路线“神仙打架”,视频生成冲上云霄
文|魏琳华 编|王一粟 一场大会,聚集了中国多模态大模型的“半壁江山”。 智源大会2025为期两天的论坛中,汇集了学界、创业公司和大厂等三方的热门选手,关于多模态的集中讨论达到了前所未有的热度。其中,…...
2025年能源电力系统与流体力学国际会议 (EPSFD 2025)
2025年能源电力系统与流体力学国际会议(EPSFD 2025)将于本年度在美丽的杭州盛大召开。作为全球能源、电力系统以及流体力学领域的顶级盛会,EPSFD 2025旨在为来自世界各地的科学家、工程师和研究人员提供一个展示最新研究成果、分享实践经验及…...
阿里云ACP云计算备考笔记 (5)——弹性伸缩
目录 第一章 概述 第二章 弹性伸缩简介 1、弹性伸缩 2、垂直伸缩 3、优势 4、应用场景 ① 无规律的业务量波动 ② 有规律的业务量波动 ③ 无明显业务量波动 ④ 混合型业务 ⑤ 消息通知 ⑥ 生命周期挂钩 ⑦ 自定义方式 ⑧ 滚的升级 5、使用限制 第三章 主要定义 …...
Java - Mysql数据类型对应
Mysql数据类型java数据类型备注整型INT/INTEGERint / java.lang.Integer–BIGINTlong/java.lang.Long–––浮点型FLOATfloat/java.lang.FloatDOUBLEdouble/java.lang.Double–DECIMAL/NUMERICjava.math.BigDecimal字符串型CHARjava.lang.String固定长度字符串VARCHARjava.lang…...
屋顶变身“发电站” ,中天合创屋面分布式光伏发电项目顺利并网!
5月28日,中天合创屋面分布式光伏发电项目顺利并网发电,该项目位于内蒙古自治区鄂尔多斯市乌审旗,项目利用中天合创聚乙烯、聚丙烯仓库屋面作为场地建设光伏电站,总装机容量为9.96MWp。 项目投运后,每年可节约标煤3670…...
P3 QT项目----记事本(3.8)
3.8 记事本项目总结 项目源码 1.main.cpp #include "widget.h" #include <QApplication> int main(int argc, char *argv[]) {QApplication a(argc, argv);Widget w;w.show();return a.exec(); } 2.widget.cpp #include "widget.h" #include &q…...
DIY|Mac 搭建 ESP-IDF 开发环境及编译小智 AI
前一阵子在百度 AI 开发者大会上,看到基于小智 AI DIY 玩具的演示,感觉有点意思,想着自己也来试试。 如果只是想烧录现成的固件,乐鑫官方除了提供了 Windows 版本的 Flash 下载工具 之外,还提供了基于网页版的 ESP LA…...
解决本地部署 SmolVLM2 大语言模型运行 flash-attn 报错
出现的问题 安装 flash-attn 会一直卡在 build 那一步或者运行报错 解决办法 是因为你安装的 flash-attn 版本没有对应上,所以报错,到 https://github.com/Dao-AILab/flash-attention/releases 下载对应版本,cu、torch、cp 的版本一定要对…...
稳定币的深度剖析与展望
一、引言 在当今数字化浪潮席卷全球的时代,加密货币作为一种新兴的金融现象,正以前所未有的速度改变着我们对传统货币和金融体系的认知。然而,加密货币市场的高度波动性却成为了其广泛应用和普及的一大障碍。在这样的背景下,稳定…...
安卓基础(aar)
重新设置java21的环境,临时设置 $env:JAVA_HOME "D:\Android Studio\jbr" 查看当前环境变量 JAVA_HOME 的值 echo $env:JAVA_HOME 构建ARR文件 ./gradlew :private-lib:assembleRelease 目录是这样的: MyApp/ ├── app/ …...
