18. 结合Selenium和YAML对页面继承对象PO的改造

18. 结合Selenium和YAML对页面继承对象PO的改造

一、架构改造核心思路

1.1 改造前后对比

硬编码元素定位
YAML配置驱动
原始PO模式
维护困难
改造后PO模式
动态加载元素

1.2 核心优势

  • 定位信息与代码解耦
  • 支持多环境配置切换
  • 提升代码可维护性
  • 实现元素配置热更新

二、PO核心类改造解析

2.1 页面基类增强

class Page:elements_yml = {}  # 子类需覆盖的配置映射elements_pool = {}  # 配置缓存池def _locator(self, expression: str = 'cp.username'):key, value = expression.split('.')# 动态加载YAML配置if key not in self.elements_pool:self.elements_pool[key] = YamlReader(self.elements_yml[key]).data# 海象运算符验证定位方法if (locator := self.elements_pool[key][value])[0] not in BY_RULES:raise Exception(f'无效定位方法: {locator}')return self.elements_pool[key][value]
关键技术点:
  • 双缓存机制(elements_ymlelements_pool
  • 表达式解析(key.value格式)
  • 定位方法白名单验证(BY_RULES

三、配置管理系统升级

3.1 setting.py核心配置

# 元素配置文件映射
YAML_ELEMENT = {'cp': join(ELEMENTS_YAML_FILE_PATH, 'CommonLoginPass.yml'),'op': join(ELEMENTS_YAML_FILE_PATH, 'oder_page.yml')
}# 合法定位方法白名单
BY_RULES = ('id', 'xpath', 'link text', 'partial link text', 'name','tag name', 'class name', 'css selector'
)

3.2 路径管理优化

# 动态路径构建
ELEMENTS_YAML_FILE_PATH = join(BASE_PATH, 'Chap5\\page')
CHAPTER_1_PATH = join(BASE_PATH, 'chap3')  # 跨平台兼容

四、页面类实现示例

4.1 登录页面改造

class CommonLoginPass(Page):elements_yml = YAML_ELEMENT  # 绑定配置文件def login(self, username: str = 'Tester'):self.element('cp.username').send_keys(username)  # 表达式驱动self.element('cp.password').send_keys(password)self.element('cp.loginBtn').click()

4.2 订单页面继承

class Oder(CommonLoginPass):def search_bug(self):self.element('op.clickOrder').click()  # 继承配置映射self.element('op.orderInput').send_keys('Tom')

五、YAML配置文件规范

5.1 元素定义标准格式

# CommonLoginPass.yml
username:- id  # 定位类型- ctl00_MainContent_username  # 定位表达式loginBtn:- id- ctl00_MainContent_login_button

5.2 配置文件结构要求

  • 二级键值为列表类型
  • 首个元素必须为BY_RULES允许的定位方法
  • 元素命名采用小驼峰格式
  • 注释说明元素用途

六、执行流程优化

6.1 元素加载流程

PageObject YamlReader Selenium YAML文件 解析表达式(cp.username) 加载CommonLoginPass.yml 返回配置数据 缓存配置 driver.find_element(id, ctl00...) PageObject YamlReader Selenium YAML文件

6.2 性能优化策略

  • 首次访问时加载配置文件
  • 内存缓存已解析配置
  • 避免重复IO操作
  • 按需加载不同模块配置

七、改造收益分析

指标改造前改造后提升率
维护成本需修改源代码仅改配置文件70%
定位信息复用类级别复用跨项目复用200%
执行效率每次重新定位缓存加速访问40%
可读性代码混杂定位业务逻辑聚焦60%

八、完整代码

"""
Python :3.13.3
Selenium: 4.31.0po.py
"""from chap3.ob import *
from setting import *
from chap5.file_reader import YamlReaderclass Page:url = Nonedriver = None# 子类重写,获取通用配置文件中具体项目的元素配置文件字典elements_yml = {}# 缓存动态读取的yaml元素配置文件的解析结果elements_pool = {}def _locator(self, expression: str = 'cp.username'):"""解析元素表达式的方法:param expression::return:"""key, value = expression.split('.')if key not in self.elements_yml:raise Exception('元素配置文件的别名:{}无法识别!'.format(key))if key not in self.elements_pool:self.elements_pool[key] = YamlReader(self.elements_yml[key]).dataif (locator := self.elements_pool[key][value])[0] not in BY_RULES:raise Exception(f'无法识别定位方法:{locator}')return locatorreturn self.elements_pool[key][value]@classmethoddef cls_locator(cls, expression: str = 'cp.username'):"""类方法版本的locator,解析元素表达式:param expression: 元素表达式,格式为'配置文件别名.元素名':return: 定位元组(定位方式, 定位表达式)"""key, value = expression.split('.')if key not in cls.elements_yml:raise Exception('元素配置文件的别名:{}无法识别!'.format(key))if key not in cls.elements_pool:cls.elements_pool[key] = YamlReader(cls.elements_yml[key]).dataif (locator := cls.elements_pool[key][value])[0] not in BY_RULES:raise Exception(f'无法识别定位方法:{locator}')return locatorreturn cls.elements_pool[key][value]@classmethoddef cls_element(cls, loc: str):return cls.driver.find_element(*cls.cls_locator(loc))def element(self, loc: str):"""定位元素的方法:param loc::return:"""return self.driver.find_element(*self._locator(loc))def elements(self, loc: str):"""定位一组元素或多个元素:param loc::return:"""return self.driver.find_element(*self._locator(loc))class CommonLoginPass(Page):url = PROJECT_Oder_URLdriver = CHROME().start_chrome_browser# username = ('id', 'ctl00_MainContent_username')# password = ('id', 'ctl00_MainContent_password')# loginBtn = ('id', 'ctl00_MainContent_login_button')elements_yml = YAML_ELEMENTdef get(self):"""打开首页地址:return:"""self.driver.get(self.url)@classmethoddef cls_get(cls):"""类方法,打开首页:return:"""cls.driver.get(cls.url)def login(self, username: str = 'Tester', password: str = 'test'):# self.element(self.username).send_keys(username)# self.element(self.password).send_keys(password)# self.element(self.loginBtn).click()self.element('cp.username').send_keys(username)self.element('cp.password').send_keys(password)self.element('cp.loginBtn').click()@classmethoddef cls_login(cls, username: str = 'Tester', password: str = 'test'):"""类方法,登录:return:"""cls.cls_element('cp.username').send_keys(username)cls.cls_element('cp.password').send_keys(password)cls.cls_element('cp.loginBtn').click()class Oder(CommonLoginPass):# clickOrder = ('xpath', '//*[@id="ctl00_menu"]/li[3]/a')# orderInput = ('id', 'ctl00_MainContent_fmwOrder_txtName')# clickProcess = ('id', 'ctl00_MainContent_fmwOrder_InsertButton')## bug_label = ('id', "ctl00_MainContent_fmwOrder_RequiredFieldValidator3")# order_label = ('xpath', '//*[@id="aspnetForm"]//td[1]/h1')## invalid_login = ('xpath', '//*[@id="ctl00_MainContent_status"]')## log_out = ('xpath', '//*[@id="ctl00_logout"]')def search_bug(self, order_input: str = 'Tom'):self.element('op.clickOrder').click()self.element('op.orderInput').send_keys(order_input)self.element('op.clickProcess').click()def logout(self):self.element('op.log_out').click()class TestOder(Oder):"""测试登录和检索bug功能"""def test_login(self):self.get()self.login()assert self.element('op.order_label').text == 'Web Orders'print('test_login is passed')def test_search(self):self.search_bug()from time import sleepsleep(4)assert self.element('op.bug_label').text == "Field 'Street' cannot be empty."print('test_search is passed')self.driver.quit()obj = TestOder()
obj.test_login()
obj.test_search()

下面是setting.py的代码:

"""
Python :3.13.3
Selenium: 4.31.0
"""# 项目地址
# 项目包和文件夹的路径
# 浏览器对象属性
# 测试套件from os.path import dirname, join# -------------------项目地址-----------------------
# 项目一的地址
PROJECT_Oder_URL = 'http://secure.smartbearsoftware.com/samples/testcomplete12/WebOrders/Login.aspx'# 项目二的地址
PROJECT_QQ_URL = ''# 项目三的地址
PROJECT_DEMO_URL = ''# -------------------项目包和文件夹的路径-----------------------
# 项目根目录
BASE_PATH = dirname(__file__)# 浏览器驱动文件地址
CHROME_DRIVER_PATH = join(BASE_PATH, 'drivers\\chrome_driver.exe')
EDGE_DRIVER_PATH = join(BASE_PATH, 'driver\\edge_driver.exe')# 项目模块路径
# 模块1路径
CHAPTER_1_PATH = join(BASE_PATH, 'chap3')
# 模块2路径
CHAPTER_2_PATH = join(BASE_PATH, 'chap4')
# 模块3路径
CHAPTER_3_PATH = join(BASE_PATH, 'chap5')# 元素配置文件的根目录
ELEMENTS_YAML_FILE_PATH = join(BASE_PATH, 'Chap5\\page')
# -------------------测试套件-----------------------
# 流程1相关测试套件
SUIT_MODULE_1 = ['test_module_1.py','test_module_2.py'
]# 流程2相关测试套件
SUIT_MODULE_2 = ['test_module_1.py','test_module_2.py','test_module_3.py'
]# 流程3相关测试套件
SUIT_MODULE_3 = ['test_module_4.py','test_module_5.py'
]# 项目一的主测试套件
SUIT_PROJECT1 = ['test_module_1.py','test_module_2.py','test_module_3.py']# 项目二的主测试套件
SUIT_PROJECT2 = SUIT_MODULE_2 + SUIT_MODULE_3# -------------------浏览器对象属性-----------------------
# 浏览器基本属性# 无头化
HEADLESS = False# 隐式等待时间
IMP_TIME = 30# 页面加载超时时间
PAGE_LOAD_TIME = 20# JS异步执行超时时间
SCRIPT_TIME_OUT = 20# 浏览器尺寸
WINDOWS_SIZE = (1024, 768)# -------------------CHROME浏览器属性-----------------------# chrome浏览器操作开关
CHROME_METHOD_MARK = True# chrome启动参数开关
CHROME_OPTION_MARK = True# chrome实验性质启动参数
CHROME_EXP = {'excludeSwitches': ['enable-automation'],# 'mobileEmulation': {'deviceName': 'iPhone 6'}}# chrome窗口大小启动参数
CHROME_WINDOWS_SIZE = (1920, 900)# chrome启动最大化参数
CHROME_START_MAX = '--start-maximized'# -------------------EDGE浏览器属性-----------------------
# -------------------FIREFOX浏览器属性-----------------------# -------------------YAML元素配置文件-----------------------
YAML_ELEMENT = {'cp': join(ELEMENTS_YAML_FILE_PATH, 'CommonLoginPass.yml'),'op': join(ELEMENTS_YAML_FILE_PATH, 'oder_page.yml')
}
# -------------------YAML元素配置文件-----------------------#-------------------WEB元素定位方法-----------------------
BY_RULES = ('id','xpath','link text','partial link text','name','tag name','class name','css selector')
#-------------------WEB元素定位方法-----------------------

两份存放元素的yaml文件:

# 登录账号
username:- id- ctl00_MainContent_username# 密码
password:- id- ctl00_MainContent_password# 登录按钮
loginBtn:- id- ctl00_MainContent_login_button
# 点击‘Oder’按钮
clickOrder:- xpath- //*[@id="ctl00_menu"]/li[3]/a# 在字段‘Customer name’输入'Tom'
orderInput:- id- ctl00_MainContent_fmwOrder_txtName# 点击'Process'按钮
clickProcess:- id- ctl00_MainContent_fmwOrder_InsertButton# 检查‘Field 'Customer name' cannot be empty.’提示是否存在
bug_label:- id- ctl00_MainContent_fmwOrder_RequiredFieldValidator3# 检查‘Web Orders’标题是否存在
order_label:- xpath- //*[@id="aspnetForm"]//td[1]/h1# 检查账号密码错误时的提示内容
invalid_login:- xpath- //*[@id="ctl00_MainContent_status"]# 点击'logout'按钮
log_out:- xpath- //*[@id="ctl00_logout"]

「小贴士」:点击头像→【关注】按钮,获取更多软件测试的晋升认知不迷路! 🚀

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/81891.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

将VMware上的虚拟机和当前电脑上的Wifi网卡处在同一个局域网下,实现同一个局域网下实现共享

什么是桥接模式:桥接模式(Bridging Mode)是一种网络连接模式,常用于虚拟机环境中,将虚拟机的虚拟网络适配器直接连接到主机的物理网络适配器上,使虚拟机能够像独立的物理设备一样直接与物理网络通信 1.打开…

gitee错误处理总结

背景 如上图,根据图片中的 Git 错误提示,我们遇到的问题是 ​本地分支落后于远程分支,导致 git push 被拒绝。 ​问题原因​ 远程仓库的 master 分支有其他人推送的新提交,而您的本地 master 分支未同步这些更新(即本…

如何提高独立服务器的安全性?

独立服务器相对于其它服务器来说,整体的硬件设备都是独立的同时还有着强大的服务器性能,其中CPU设备能够决定着服务器的运算能力,所以独立服务器的安全性受到企业格外的重视,严重的话会给企业造成巨大的资金损失。 那么&#xff0…

Spark,集群搭建-Standalone

以下是 Spark Standalone 集群搭建 的详细步骤(基于 Linux 系统,以伪分布式为例): 一、环境准备 1. 硬件要求 - 至少 2 台节点(1 台 Master,1 台 Worker,可扩展)。 - 每节点配置…

如何在WordPress中使用ChatGPT

ChatGPT 自 2022 年 11 月问世以来,极大地影响了我们的创作方式。ChatGPT 可以帮助您制作大纲、标题、段落或完整的博客文章,各地的数字创作者都在热衷于使用人工智能(AI)创建内容。随着人工智能的不断发展,我们看到了…

spring5-配外部文件-spEL-工厂bean-FactoryBean-注解配bean

spring配外部文件 我们先在Spring里配置一个数据源 1.导c3p0包,这里我们先学一下hibernate持久化框架&#xff0c;以后用mybites. <dependency><groupId>org.hibernate</groupId><artifactId>hibernate-core</artifactId><version>5.2.…

GStreamer (三)常⽤插件

常⽤插件 1、Source1.1、filesrc1.2. videotestsrc1.3. v4l2src1.4. rtspsrc和rtspclientsink 2、 Sink2.1. filesink2.2. fakesink2.3. xvimagesink2.4. kmssink2.5. waylandsink2.6. rkximagesink2.7. fpsdisplaysink 3 、视频推流/拉流3.1. 本地推流/拉流3.1.1 USB摄像头3.1…

【EDA软件】【联合Modelsim仿真使用方法】

背景 业界EDA工具仿真功能是必备的&#xff0c;例如Vivado自带仿真工具&#xff0c;且无需联合外部仿真工具&#xff0c;例如MoodelSim。 FUXI工具仿真功能需要联合Modelsim&#xff0c;才能实现仿真功能。 方法一&#xff1a;FUXI联合ModelSim 1 添加testbench文件 新建to…

国产化Excel处理组件Spire.XLS for .NET系列教程:通过 C# 将 TXT 文本转换为 Excel 表格

在数据处理和管理场景中&#xff0c;将原始文本文件&#xff08;TXT&#xff09;高效转换为结构化的 Excel 电子表格是一项常见要求。对于那些需要自动生成报表或者处理日志文件的开发人员而言&#xff0c;借助 C# 实现 TXT 到 Excel 的转换工作&#xff0c;可以简化数据组织和…

DeepSeek 的强化学习优化策略:RLHF 与 DPO 的应用

DeepSeek 的强化学习优化策略&#xff1a;RLHF 与 DPO 的应用 系统化学习人工智能网站&#xff08;收藏&#xff09;&#xff1a;https://www.captainbed.cn/flu 文章目录 DeepSeek 的强化学习优化策略&#xff1a;RLHF 与 DPO 的应用摘要引言技术原理对比1. RLHF&#xff1a…

c: 分号的歧义

最近看到一个关于某些语言里的分号问题&#xff0c;比如下面一个作者就是无意识的每行后面多加了分号导致问题。 其实python的语法可以更好的规避这种潜意识&#xff0c;因为根本就不需要每行后面加分号的意识&#xff0c;也就不需要开发者习惯这种意识。 所以&#xff0c;最后…

Elasticsearch 实战面试题,每个题目都会单独解析

Elasticsearch 在 Java 中最常用的客户端是什么&#xff1f;如何初始化一个 RestHighLevelClient&#xff1f;如何用 Spring Boot 快速集成 Elasticsearch&#xff1f;Spring Data Elasticsearch 如何定义实体类与索引的映射&#xff1f; ES的倒排索引和正排索引的区别及适用场…

拉普拉斯高斯(LoG)滤波器掩模的注意事项

目录 问题&#xff1a; 解答&#xff1a; 一、高斯函数归一化&#xff1a;消除幅度偏差 1. 归一化的定义 2. 为何必须归一化&#xff1f; 二、拉普拉斯系数和为零&#xff1a;抑制直流项干扰 1. 拉普拉斯算子的特性 2. 系数和不为零的后果 三、直流项如何影响零交叉点&…

运维实施35-磁盘管理

了解磁盘 硬盘的接口类型 接口类型发展方向应用场景IDESATA I/II/III个人PC机SCSISAS服务器上 磁盘命名规则 OSIDE(并口)SATA(串口)SCSIRHEL5/dev/hda/dev/sda/dev/sdaRHEL6/dev/sda/dev/sda/dev/sdaRHEL7/dev/sda/dev/sda/dev/sda 磁盘设备的命名 /dev/sda2 s 硬件接口…

API面临哪些风险,如何做好API安全?

API面临的风险 API&#xff08;应用程序编程接口&#xff09;在现代软件开发和集成中扮演着至关重要的角色&#xff0c;但同时也面临着多种安全风险&#xff0c;主要包括以下几个方面&#xff1a; 数据泄露风险&#xff1a; API通常涉及敏感数据的传输和交换&#xff0c;如用…

`application-{env}.yml` 配置文件来实现多环境配置

在 Spring Boot 应用中&#xff0c;使用多套 application-{env}.yml 配置文件来实现多环境配置是一种常见且推荐的做法。这种方式可以帮助你根据不同的环境&#xff08;如开发、测试、生产等&#xff09;加载不同的配置&#xff0c;从而实现环境隔离和灵活配置。以下是如何通过…

野火鲁班猫(arrch64架构debian)从零实现用MobileFaceNet算法进行实时人脸识别(一)conda环境搭建

先安装miniconda wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-aarch64.sh chmod x Miniconda3-latest-Linux-aarch64.sh bash Miniconda3-latest-Linux-aarch64.sh source ~/.bashrc conda --version按照MobileFaceNet的github官方指南&#xff0c;需要…

目标检测 Lite-DETR(2023)详细解读

文章目录 迭代高级特征跨尺度融合高效的低层次特征跨尺度融合KDA&#xff1a;Key-aware Deformable Attention 论文翻译&#xff1a; CVPR 2023 | Lite DETR&#xff1a;计算量减少60%&#xff01;高效交错多尺度编码器-CSDN博客 DINO团队的 &#xff08;Lightweight Transfo…

【Git】远程操作

Git 是一个分布式版本控制系统 可以简单理解为&#xff0c;每个人的电脑上都是一个完整的版本库&#xff0c;这样在工作时&#xff0c;就不需要联网 了&#xff0c;因为版本库就在自己的电脑上。 因此&#xff0c; 多个人协作的方式&#xff0c;譬如说甲在自己的电脑上改了文件…

华为云Flexus+DeepSeek征文|基于华为云Flexus云服务的Dify 构建智能客服助手

目录 一、构建智能客服助手应用 二、构建智能客服助手提示词 2.1 什么是智能客服助手&#xff1f; 2.2 生成智能客服助手提示词 三、访问智能客服助手 3.1 智能客服助手发布 3.2 智能客服助手聊天 3.3 开启新会话 四、总结 本篇文章主要基于华为云Flexus云服务的Dify 构…