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 联系我领取学习资料 …...
Python办公自动化:使用openpyxl 创建与保存 Excel 工作簿
1 创建新的工作簿 在开始任何 Excel 操作之前,首先需要创建一个工作簿。openpyxl 提供了简单的接口来创建新的工作簿。 创建一个空白的工作簿 我们可以使用 openpyxl.Workbook() 来创建一个新的空白工作簿。以下是一个简单的示例: import openpyxl# …...
【张】#11 Union 共用体
Union 共用体可以存储不同的数据类型,但只能同时存储其中的一种类型。 #include <iostream> using namespace std;struct Product {char productName[20];int type;//1 int ,else charunion{int id_int;char id_chars[20];}; };int main(){Product product; …...
Xcode 在原生集成flutter项目
笔者公司有一个从2017年就开始开发的iOS和安卓原生项目,现在计划从外到内开始进行项目迁徙。 1》从gitee拉取flutter端的代码;(Android报错Exception: Podfile missing) 2》替换Xcode里的cocopods里Podfile的路径 然后报警 然后…...
ES6的promise
Promise是什么 1、Promise是js中的一个原生对象,是一种异步编程的解决方案。可以替换掉传统的回调函数解决方案,将异步操作以同步的流程表达出来。 2、Promise有三种状态:pending(初始化)、fulfilled(成功)、rejected(失败) 可以通过resolve(…...
轻松找回:如何在PostgreSQL 16中重置忘记的数据库密码
目录 1. 引言2. PostgreSQL 16的新特性简介3. 解决方法概述4. 方法一:通过修改pg_hba.conf文件重置密码5. 方法二:通过命令行进入单用户模式6. 方法三:使用pgAdmin工具重置密码7. 总结与最佳实践写在以后 1. 引言 你有没有过这样的经历&…...
EVAL长度突破限制
目录 突破15位限制 代码 绕过方式 第一种(使用echo执行) 第二种(使用file_get_content追加文件后进行问件包含) 第三种(使用usort可变长参数) 突破7位限制 第一种(可以使用>创建文件…...
如何判断树上一个点是否在直径上
# 旅游规划 ## 题目描述 W市的交通规划出现了重大问题,市政府下定决心在全市各大交通路口安排疏导员来疏导密集的车流。但由于人员不足,W市市长决定只在最需要安排人员的路口安排人员。 具体来说,W市的交通网络十分简单,由n个…...
docker 部署 RabbitMQ
命令 docker run -d --namerabbitmq \ -p 5671:5671 -p 5672:5672 -p 4369:4369 \ -p 15671:15671 -p 15672:15672 -p 25672:25672 \ -e RABBITMQ_DEFAULT_USERusername\ -e RABBITMQ_DEFAULT_PASSpassword\ -v /usr/local/rabbitmq/data:/var/lib/rabbitmq \ -v /usr/local/r…...
设计模式 - 过滤器模式
💝💝💝首先,欢迎各位来到我的博客!本文深入理解设计模式原理、应用技巧、强调实战操作,提供代码示例和解决方案,适合有一定编程基础并希望提升设计能力的开发者,帮助读者快速掌握并灵活运用设计模式。 💝💝💝如有需要请大家订阅我的专栏【设计模式】哟!我会定…...
使用 Locust 进行本地压力测试
在应用开发和运维过程中,了解应用在高负载情况下的表现至关重要。压力测试可以帮助你识别性能瓶颈和潜在问题。本文将介绍如何使用 Locust 工具进行本地压力测试,模拟高并发场景,并分析测试结果。 1. 什么是 Locust? Locust 是一…...
怎样制作小程序软件/优化大师好用吗
归根结底,用CSS绘制三角形,就是利用边框(border)特性进行绘制 平时我们用border可能用的很多,但是一般都会统一设置所有边框的颜色,很少研究每个方向的边框具体样式是怎样的。现在先来看一个简单的例子,我们分别设置每…...
在哪里进行网站域名的实名认证/网站推广是什么
JavaScript是一种基于对象的脚本编程语言,是浏览器上的程序语言。当web容器输出内容到浏览器时,这个内容是包含js源代码的,此时,JavaScript可以操作浏览器上的一切内容,在浏览器上提供用户交互,页面美化&am…...
做网站建设优化的公司/免费个人网站注册
1.内核对内存的分配overcommit_memory文件指定了内核针对内存分配的策略,其值可以是0、1、2。0, 表示内核将检查是否有足够的可用内存供应用进程使用;如果有足够的可用内存,内存申请允许;否则,内存申请失败…...
织梦做网站视频教程/营销推广方案设计
方一 Integer[] xnew Integer[]{4,6,9,10}; Set<Integer> set new HashSet<>() ; Collections.addAll(set,x);for(Integer ele:set){System.out.println(ele); }方二 Set<Integer> set new HashSet<>(Arrays.asList(4,6,9,10)) ;...
php做的网站怎么加密/百度seo排名优化公司
Linux中的配置文件一般为:纯文本格式 XML格式Bash配置文件:1) profile类:交互式登录用户/etc/profile(它还包含-/etc/profile.d/*.sh对全局用户有效)--~/.bash_profile(仅对某个用户有效,编辑它还可以针对特定用户在登录时显示…...
如何做优品快报下的子网站/深圳百度竞价推广
分布式一致性协议 二阶段提交协议(2pc)三阶段提交协议(3pc)paxoszab在分布式系统中,每个机器都可以确定自己进行的事务操作是否成功,但是无法直接了解其他机器的操作结果。因此,当一个分布式事务…...