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 联系我领取学习资料 …...
Docker 离线安装指南
参考文章 1、确认操作系统类型及内核版本 Docker依赖于Linux内核的一些特性,不同版本的Docker对内核版本有不同要求。例如,Docker 17.06及之后的版本通常需要Linux内核3.10及以上版本,Docker17.09及更高版本对应Linux内核4.9.x及更高版本。…...
【Oracle APEX开发小技巧12】
有如下需求: 有一个问题反馈页面,要实现在apex页面展示能直观看到反馈时间超过7天未处理的数据,方便管理员及时处理反馈。 我的方法:直接将逻辑写在SQL中,这样可以直接在页面展示 完整代码: SELECTSF.FE…...
关于iview组件中使用 table , 绑定序号分页后序号从1开始的解决方案
问题描述:iview使用table 中type: "index",分页之后 ,索引还是从1开始,试过绑定后台返回数据的id, 这种方法可行,就是后台返回数据的每个页面id都不完全是按照从1开始的升序,因此百度了下,找到了…...
2024年赣州旅游投资集团社会招聘笔试真
2024年赣州旅游投资集团社会招聘笔试真 题 ( 满 分 1 0 0 分 时 间 1 2 0 分 钟 ) 一、单选题(每题只有一个正确答案,答错、不答或多答均不得分) 1.纪要的特点不包括()。 A.概括重点 B.指导传达 C. 客观纪实 D.有言必录 【答案】: D 2.1864年,()预言了电磁波的存在,并指出…...
Nuxt.js 中的路由配置详解
Nuxt.js 通过其内置的路由系统简化了应用的路由配置,使得开发者可以轻松地管理页面导航和 URL 结构。路由配置主要涉及页面组件的组织、动态路由的设置以及路由元信息的配置。 自动路由生成 Nuxt.js 会根据 pages 目录下的文件结构自动生成路由配置。每个文件都会对…...
Linux-07 ubuntu 的 chrome 启动不了
文章目录 问题原因解决步骤一、卸载旧版chrome二、重新安装chorme三、启动不了,报错如下四、启动不了,解决如下 总结 问题原因 在应用中可以看到chrome,但是打不开(说明:原来的ubuntu系统出问题了,这个是备用的硬盘&a…...
短视频矩阵系统文案创作功能开发实践,定制化开发
在短视频行业迅猛发展的当下,企业和个人创作者为了扩大影响力、提升传播效果,纷纷采用短视频矩阵运营策略,同时管理多个平台、多个账号的内容发布。然而,频繁的文案创作需求让运营者疲于应对,如何高效产出高质量文案成…...
20个超级好用的 CSS 动画库
分享 20 个最佳 CSS 动画库。 它们中的大多数将生成纯 CSS 代码,而不需要任何外部库。 1.Animate.css 一个开箱即用型的跨浏览器动画库,可供你在项目中使用。 2.Magic Animations CSS3 一组简单的动画,可以包含在你的网页或应用项目中。 3.An…...
C++课设:简易日历程序(支持传统节假日 + 二十四节气 + 个人纪念日管理)
名人说:路漫漫其修远兮,吾将上下而求索。—— 屈原《离骚》 创作者:Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder😊) 专栏介绍:《编程项目实战》 目录 一、为什么要开发一个日历程序?1. 深入理解时间算法2. 练习面向对象设计3. 学习数据结构应用二、核心算法深度解析…...
【Android】Android 开发 ADB 常用指令
查看当前连接的设备 adb devices 连接设备 adb connect 设备IP 断开已连接的设备 adb disconnect 设备IP 安装应用 adb install 安装包的路径 卸载应用 adb uninstall 应用包名 查看已安装的应用包名 adb shell pm list packages 查看已安装的第三方应用包名 adb shell pm list…...
