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

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 保险会计及其特殊性

保险会计是将会计理论专门应用于保险公司的专业会计领域,它是会计学的一个重要分支。作为一个分支,保险会计具有独特的特性,这些特性主要表现在以下几个方面: 产品的无形性:保险产品本质上是一种无形的商品&#xff0c…...

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&#xff…...

如何妙用哈希表来优化遍历查找过程?刷题感悟总结,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 开发者大会上&#xff0c;看到基于小智 AI DIY 玩具的演示&#xff0c;感觉有点意思&#xff0c;想着自己也来试试。 如果只是想烧录现成的固件&#xff0c;乐鑫官方除了提供了 Windows 版本的 Flash 下载工具 之外&#xff0c;还提供了基于网页版的 ESP LA…...

解决本地部署 SmolVLM2 大语言模型运行 flash-attn 报错

出现的问题 安装 flash-attn 会一直卡在 build 那一步或者运行报错 解决办法 是因为你安装的 flash-attn 版本没有对应上&#xff0c;所以报错&#xff0c;到 https://github.com/Dao-AILab/flash-attention/releases 下载对应版本&#xff0c;cu、torch、cp 的版本一定要对…...

稳定币的深度剖析与展望

一、引言 在当今数字化浪潮席卷全球的时代&#xff0c;加密货币作为一种新兴的金融现象&#xff0c;正以前所未有的速度改变着我们对传统货币和金融体系的认知。然而&#xff0c;加密货币市场的高度波动性却成为了其广泛应用和普及的一大障碍。在这样的背景下&#xff0c;稳定…...

安卓基础(aar)

重新设置java21的环境&#xff0c;临时设置 $env:JAVA_HOME "D:\Android Studio\jbr" 查看当前环境变量 JAVA_HOME 的值 echo $env:JAVA_HOME 构建ARR文件 ./gradlew :private-lib:assembleRelease 目录是这样的&#xff1a; MyApp/ ├── app/ …...