UI自动化测试:Selenium+PO模式+Pytest+Allure整合
本人目前工作中未涉及到WebUI自动化测试,但为了提升自己的技术,多学习一点还是没有坏处的,废话不多说了,目前主流的webUI测试框架应该还是selenium,考虑到可维护性、拓展性、复用性等,我们采用PO模式去写我们的脚本,本文档也主要整合了Selenium+PO模式+Pytest+Allure,下面我们进入正题。注:文章末尾附Github地址
技术前提:python、selenium、pytest基础知识
1. 项目结构目录:
2. PO模式介绍
PO模式特点:
- 易于维护
- 复用性高
- 脚本易于阅读理解
PO模式要素:
1. 在PO模式中抽象封装成一个BasePage类,该基类应该拥有一个只实现 webdriver 实例的属性
2. 每个一个 pag 都继承BasePage,通过driver来管理本page中元素将page中的操作封装成一个个的方法
3. TestCase依赖 page 类,从而实现相应的测试步骤
现在我也找了很多测试的朋友,做了一个分享技术的交流群,共享了很多我们收集的技术文档和视频教程。
如果你不想再体验自学时找不到资源,没人解答问题,坚持几天便放弃的感受
可以加入我们一起交流。而且还有很多在自动化,性能,安全,测试开发等等方面有一定建树的技术大牛
分享他们的经验,还会分享很多直播讲座和技术沙龙
可以免费学习!划重点!开源的!!!
qq群号:110685036
3. BasePage 页面封装
import logging
import os
import time
from datetime import datetime
from time import sleep
from selenium import webdriver
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.wait import WebDriverWait
from selenium.common.exceptions import TimeoutException, NoSuchElementException
from Utils.myLog import MyLog"""此类封装所有操作,所有页面继承该类
"""class BasePage(object):def __init__(self, driver):self.logger = MyLog().getLog()self.driver = driver# 等待元素可见def wait_eleVisible(self, loc, timeout=30, poll_frequency=0.5, model=None):""":param loc:元素定位表达;元组类型,表达方式(元素定位类型,元素定位方法):param timeout:等待的上限:param poll_frequency:轮询频率:param model:等待失败时,截图操作,图片文件中需要表达的功能标注:return:None"""self.logger.info(f'等待"{model}"元素,定位方式:{loc}')try:start = datetime.now()WebDriverWait(self.driver, timeout, poll_frequency).until(EC.visibility_of_element_located(loc))end = datetime.now()self.logger.info(f'等待"{model}"时长:{end - start}')except TimeoutException:self.logger.exception(f'等待"{model}"元素失败,定位方式:{loc}')# 截图self.save_webImgs(f"等待元素[{model}]出现异常")raise# 等待元素不可见def wait_eleNoVisible(self, loc, timeout=30, poll_frequency=0.5, model=None):""":param loc:元素定位表达;元组类型,表达方式(元素定位类型,元素定位方法):param timeout:等待的上限:param poll_frequency:轮询频率:param model:等待失败时,截图操作,图片文件中需要表达的功能标注:return:None"""logging.info(f'等待"{model}"消失,元素定位:{loc}')try:start = datetime.now()WebDriverWait(self.driver, timeout, poll_frequency).until_not(EC.visibility_of_element_located(loc))end = datetime.now()self.logger.info(f'等待"{model}"时长:{end - start}')except TimeoutException:self.logger.exception(f'等待"{model}"元素失败,定位方式:{loc}')# 截图self.save_webImgs(f"等待元素[{model}]消失异常")raise# 查找一个元素elementdef find_element(self, loc, model=None):self.logger.info(f'查找"{model}"元素,元素定位:{loc}')try:return self.driver.find_element(*loc)except NoSuchElementException:self.logger.exception(f'查找"{model}"元素失败,定位方式:{loc}')# 截图self.save_webImgs(f"查找元素[{model}]异常")raise# 查找元素elementsdef find_elements(self, loc, model=None):self.logger.info(f'查找"{model}"元素集,元素定位:{loc}')try:return self.driver.find_elements(*loc)except NoSuchElementException:self.logger.exception(f'查找"{model}"元素集失败,定位方式:{loc}')# 截图self.save_webImgs(f"查找元素集[{model}]异常")raise# 输入操作def input_text(self, loc, text, model=None):# 查找元素ele = self.find_element(loc, model)# 输入操作self.logger.info(f'在"{model}"输入"{text}",元素定位:{loc}')try:ele.send_keys(text)except:self.logger.exception(f'"{model}"输入操作失败!')# 截图self.save_webImgs(f"[{model}]输入异常")raise# 清除操作def clean_inputText(self, loc, model=None):ele = self.find_element(loc, model)# 清除操作self.logger.info(f'清除"{model}",元素定位:{loc}')try:ele.clear()except:self.logger.exception(f'"{model}"清除操作失败')# 截图self.save_webImgs(f"[{model}]清除异常")raise# 点击操作def click_element(self, loc, model=None):# 先查找元素在点击ele = self.find_element(loc, model)# 点击操作self.logger.info(f'点击"{model}",元素定位:{loc}')try:ele.click()except:self.logger.exception(f'"{model}"点击失败')# 截图self.save_webImgs(f"[{model}]点击异常")raise# 获取文本内容def get_text(self, loc, model=None):# 先查找元素在获取文本内容ele = self.find_element(loc, model)# 获取文本self.logger.info(f'获取"{model}"元素文本内容,元素定位:{loc}')try:text = ele.textself.logger.info(f'获取"{model}"元素文本内容为"{text}",元素定位:{loc}')return textexcept:self.logger.exception(f'获取"{model}"元素文本内容失败,元素定位:{loc}')# 截图self.save_webImgs(f"获取[{model}]文本内容异常")raise# 获取属性值def get_element_attribute(self, loc, name, model=None):# 先查找元素在去获取属性值ele = self.find_element(loc, model)# 获取元素属性值self.logger.info(f'获取"{model}"元素属性,元素定位:{loc}')try:ele_attribute = ele.get_attribute(name)self.logger.info(f'获取"{model}"元素"{name}"属性集为"{ele_attribute}",元素定位:{loc}')return ele_attributeexcept:self.logger.exception(f'获取"{model}"元素"{name}"属性失败,元素定位:{loc}')# 截图self.save_webImgs(f"获取[{model}]属性异常")raise# iframe 切换def switch_iframe(self, frame_refer, timeout=30, poll_frequency=0.5, model=None):# 等待 iframe 存在self.logger.info('iframe 切换操作:')try:# 切换 == index\name\id\WebElementWebDriverWait(self.driver, timeout, poll_frequency).until(EC.frame_to_be_available_and_switch_to_it(frame_refer))sleep(0.5)self.logger.info('切换成功')except:self.logger.exception('iframe 切换失败!!!')# 截图self.save_webImgs(f"iframe切换异常")raise# 窗口切换 = 如果是切换到新窗口,new. 如果是回到默认的窗口,defaultdef switch_window(self, name, cur_handles=None, timeout=20, poll_frequency=0.5, model=None):"""调用之前要获取window_handles:param name: new 代表最新打开的一个窗口. default 代表第一个窗口. 其他的值表示为窗口的 handles:param cur_handles::param timeout:等待的上限:param poll_frequency:轮询频率:param model:等待失败时,截图操作,图片文件中需要表达的功能标注:return:"""try:if name == 'new':if cur_handles is not None:self.logger.info('切换到最新打开的窗口')WebDriverWait(self.driver, timeout, poll_frequency).until(EC.new_window_is_opened(cur_handles))window_handles = self.driver.window_handlesself.driver.swich_to.window(window_handles[-1])else:self.logger.exception('切换失败,没有要切换窗口的信息!!!')self.save_webImgs("切换失败_没有要切换窗口的信息")raiseelif name == 'default':self.logger.info('切换到默认页面')self.driver.switch_to.default()else:self.logger.info('切换到为 handles 的窗口')self.driver.swich_to.window(name)except:self.logger.exception('切换窗口失败!!!')# 截图self.save_webImgs("切换失败_没有要切换窗口的信息")raise# 截图def save_webImgs(self, model=None):# filepath = 指图片保存目录/model(页面功能名称)_当前时间到秒.png# 截图保存目录# 拼接日志文件夹,如果不存在则自动创建cur_path = os.path.dirname(os.path.realpath(__file__))now_date = time.strftime('%Y-%m-%d', time.localtime(time.time()))screenshot_path = os.path.join(os.path.dirname(cur_path), f'Screenshots\\{now_date}')if not os.path.exists(screenshot_path):os.mkdir(screenshot_path)# 当前时间dateNow = time.strftime('%Y%m%d_%H%M%S', time.localtime(time.time()))# 路径filePath = '{}\\{}_{}.png'.format(screenshot_path, model, dateNow)try:self.driver.save_screenshot(filePath)self.logger.info(f"截屏成功,图片路径为{filePath}")except:self.logger.exception('截屏失败!')# 退出def get_driver(self):return self.driver
4. 页面继承BasPage
from Common.basePage import BasePage
from selenium.webdriver.common.by import By
from time import sleepclass BaiduIndex(BasePage):"""页面元素"""# 百度首页链接baidu_index_url = "https://www.baidu.com"# 搜索框search_input = (By.ID, "kw")# "百度一下"按钮框search_button = (By.ID, "su")# 查询操作def search_key(self, search_key):self.logger.info("【===搜索操作===】")# 等待用户名文本框元素出现self.wait_eleVisible(self.search_input, model='搜索框')# 输入内容self.input_text(self.search_input, "阿崔", model="搜索框")# 清除文本框内容self.clean_inputText(self.search_input, model='搜索框')# 输入用户名self.input_text(self.search_input, text=search_key, model='搜索框')# 等待搜索按钮出现self.wait_eleVisible(self.search_button, model='"百度一下"搜索按钮')# 点击搜索按钮self.click_element(self.search_button, model='"百度一下"搜索按钮')# 搜索后等待界面加载完成self.driver.implicitly_wait(10)sleep(3)
5. pytest+allure编写测试用例
注:Pytest整合Allure教程请参考:https://www.cnblogs.com/huny/p/13752406.html
import os
import time
import pytest
import allure
from time import sleep
from selenium import webdriver
from PageObject.baiduIndex import BaiduIndexdriver = webdriver.Chrome()
baidu_index = BaiduIndex(driver)@pytest.fixture(scope="class")
def init():# 打开浏览器,访问登录页面baidu_index.logger.info("\nWebDriver 正在初始化...")driver.get(baidu_index.baidu_index_url)baidu_index.logger.info(f"打开链接: {baidu_index.baidu_index_url}...")# 窗口最大化driver.maximize_window()# 隐式等待driver.implicitly_wait(10)baidu_index.logger.info("WebDriver 初始化完成!")yielddriver.quit()baidu_index.logger.info("WebDriver 成功退出...")@allure.feature("百度搜索")
class TestBaiduSearch:@allure.story("搜索指定关键字")@pytest.mark.baidu_search@pytest.mark.parametrize("key_word", ["哈哈","呵呵",], )def test_search(self, init, key_word):# @pytest.mark.parametrize 参数化baidu_index.search_key(key_word)web_title = driver.titleassert "哈哈_百度搜索" == web_title
6. 生成Allure测试报告
Github地址:https://github.com/Zimo6/Selenium_Demo
如果我的博客对你有帮助、如果你喜欢我的博客内容,请 “点赞” “评论” “收藏” 一键三连哦!
相关文章:

UI自动化测试:Selenium+PO模式+Pytest+Allure整合
本人目前工作中未涉及到WebUI自动化测试,但为了提升自己的技术,多学习一点还是没有坏处的,废话不多说了,目前主流的webUI测试框架应该还是selenium,考虑到可维护性、拓展性、复用性等,我们采用PO模式去写我…...

【排序算法】详解冒泡排序及其多种优化稳定性分析
文章目录 算法原理细节分析优化1优化2算法复杂度分析稳定性分析总结 算法原理 冒泡排序(Bubble Sort) 就是从序列中的第一个元素开始,依次对相邻的两个元素进行比较,如果前一个元素大于后一个元素则交换它们的位置。如果前一个元素小于或等于后一个元素…...

使用 Go 和 Wails 构建跨平台桌面应用程序
由于多种原因,Electron 曾经(并且仍然)大受欢迎。首先,其跨平台功能使开发人员能够从单个代码库支持 Linux、Windows 和 macOS。最重要的是,它对于熟悉 Javascript 的开发人员来说有一个精简的学习曲线。 尽管它有其缺…...

花2个月时间学习,面华为测开岗要30k,面试官竟说:你不是在搞笑。。。
背景介绍 计算机专业,代码能力一般,之前有过两段实习以及一个学校项目经历。第一份实习是大二暑期在深圳的一家互联网公司做前端开发,第二份实习由于大三暑假回国的时间比较短(小于两个月),于是找的实习是…...

【Python学习笔记】字符串
1. 字符串定义 可以用双引号 、 单三引号、双三引号,下面的定义都是正确的 "你好" 你好 """你好"""其中三引号可以 直接写内容有多行 的字符串。如下 letter 刘总:您好!您发的货我们已经收到&am…...

【AUTOSAR中断管理】TC3XX中断系统介绍
摘要 这段文本主要介绍了AURIX TC3XX的中断系统(Interrupt Router,简称IR)以及中断注册的过程以及举例说明中断机制。 AURIX TC3XX 中断系统(Interrupt Router)介绍 流程图描述中断路由器(IR)处理服务请求并与服务提供者交互。 中断系统的作用是将service request进行…...

Unity实现摄像机向屏幕中间发射射线射击物体
1.创建一个准星放在屏幕中间 外部找个PNG透明图,拖到Unity文件夹,右上角改成精灵sprite2d 2.添加到UI画布 3.写脚本 首先,我们需要引入一些 "工具",就像我们在玩游戏时要先下载游戏客户端一样。这里的 "工具&quo…...

测试时数据增广(TTA)与mmdetection3d中的实现
1. 测试时数据增广 测试时数据增广(TTA)在测试时使用数据增广技术获取同一数据的多个“变体”,使用同一网络在这些“变体”以及原始数据上进行推断,最后整合所有结果作为该原始数据最终的预测结果。 TTA类似于集成学习,…...

深入探索BP神经网络【简单原理、实际应用和Python示例】
人工神经网络(Artificial Neural Networks)是一种受到生物神经网络启发的机器学习模型,它的应用范围广泛,包括图像识别、语音识别、自然语言处理等领域。其中,BP神经网络(Backpropagation Neural Network&a…...

【LVGL】SquareLine Studio入门基础操作
1.SquareLine Studio基础 在这篇文章中将介绍SquareLine Studio的基础操作、解释如何加载一个项目、布局结构。 启动软件后,可以加载之前的项目、创建项目、加载一个示例。 这里以打开示例audio_mixer为例,可以双击该项目打开或者选中该项目点击右下角的【创建】按…...

【单片机】19-TFT彩屏
一、背景知识--显示器 1.什么是TFT (1)LCD显示器的构成:液晶面板驱动器【电压驱动】控制器【逻辑控制】 (2)液晶面板大致分为:TN,TFT,IPS等 (3)驱动器是跟随…...

高质量!推荐一些免费自学网站
大家好,我是 jonssonyan 说到自学网站,大家第一印象肯定是”菜鸟教程“、”w3school“、B 站大学。这些教程当然非常的好,而且适合入门学习,但是存在一些缺点,第一,知识点比较分散,没有一个整体…...

Linux之open/close/read/write/lseek记录
一、文件权限 这里不做过多描述,只是简单的记录,因为下面的命令会涉及到。linux下一切皆是文件包括文本、硬件设备、管道、数据库、socket等。通过ls -l 命令可以查看到以下信息 drwxrwxrwx 1 root root 0 Oct 10 17:06 open -rwxrwxrwx 1 root roo…...

3D调研-摄像头
参考资料: 来源1:https://leap2.ultraleap.com/leap-motion-controller-2 来源2: Gemini 2 _双目结构光相机_机器人感知-奥比中光官网 来源3: 国内外深度相机大盘点,仅用于学习科普!--机器视觉网 来源4&…...

光耦合器继电器与传统继电器:哪种最适合您的项目?
在电子和电气工程领域,继电器的选择可以显着影响项目的性能和安全性。两种常见类型的继电器是光耦合器继电器和传统机电继电器。每个都有其优点和缺点,因此选择过程对于项目的成功结果至关重要。 光耦合器继电器:基础知识 光耦合器继电器&…...

分享关于职场心态
1.解决问题而不是解释原因 2.秉承工匠思维而不是激情思维 什么是工匠思维? 工匠思维(The craftsman mindset)对待职业生涯的一种方式;是以产出为中心的职业观,关注自己给世界(工作)带来的价值…...

OK3568 UBUNTU 安装使用I2C-TOOLS
1. 安装 sudo apt-get update sudo apt-get install i2c-tools 使用I2Ctools 参考:https://blog.csdn.net/anyuliuxing/article/details/106382827 i2c-tools 是一组用于在Linux系统中进行I2C(Inter-Integrated Circuit)总线设备操作和调试…...

mysql面试题53:一个6亿的表a,一个3亿的表b,通过外间tid关联,你如何最快的查询出满足条件的第50000到第50200中的这200条数据记录
该文章专注于面试,面试只要回答关键点即可,不需要对框架有非常深入的回答,如果你想应付面试,是足够了,抓住关键点 面试官:一个6亿的表a,一个3亿的表b,通过外间tid关联,你如何最快的查询出满足条件的第50000到第50200中的这200条数据记录 可以按照以下步骤进行: 确保…...

Docker服务更新与发现
一,docker-consul简介 这是一个基于分布式的服务发现和管理工具,它具有快速构建分布式框架,提供服务发现和服务治理等特点。同时consul还提供了可靠的保证,多数据中心和强大的API以满足高可用,分布式环境下的需求。 …...

【2023集创赛】安谋科技杯二等奖作品: 智能体感游戏机
本文为2023年第七届全国大学生集成电路创新创业大赛(“集创赛”)安谋科技杯二等奖作品分享,参加极术社区的【有奖征集】分享你的2023集创赛作品,秀出作品风采,分享2023集创赛作品扩大影响力,更有丰富电子礼…...

如何使用前端包管理器(如npm、Yarn)?
聚沙成塔每天进步一点点 ⭐ 专栏简介 前端入门之旅:探索Web开发的奇妙世界 欢迎来到前端入门之旅!感兴趣的可以订阅本专栏哦!这个专栏是为那些对Web开发感兴趣、刚刚踏入前端领域的朋友们量身打造的。无论你是完全的新手还是有一些基础的开发…...

Codeforces Round 903 (Div. 3) C(矩形旋转之后对应的坐标)
题目链接:Codeforces Round 903 (Div. 3) C 题目: 思想: 旋转之后对应的坐标: (i,j)(n1-j,i)(n1-i,n1-j)(j…...

月薪过万的Java面试
写了一个月,篇幅太长了,都写不下了,被逼无奈,只能拆分 面试题: HashMap底层实现原理,红黑树,B树,B树的结构原理,volatile关键字,CAS(比较与…...

html进阶语法
html进阶 列表、表格、表单 目标:掌握嵌套关系标签的写法,使用列表标签布局网页 01-列表 作用:布局内容排列整齐的区域。 列表分类:无序列表、有序列表、定义列表。 无序列表 作用:布局排列整齐的不需要规定顺序的…...

博客系统(java,MySQL,HTML)
项目展示: 1.输入 http://127.0.0.1:8080/blog_system/login.html 即可进入登录页面 2.输入正确的用户名和密码后进入博客列表页 要是用户名或密码输入错误,会弹出错误提示框 3.点击查看全文,可以进入博客详情页查看详细信息 4.点击写博客&a…...

Android Studio SDKGradleJDK等工具的正确使用
AS在安装使用过程中可能会占用C盘大量空间,对于C盘容量本来就小的人来说非常不友好,其实我们可以自定义安装路径 SDK默认安装位置 各种版本和NDK也会安装到这个路径 SDK版本选择性安装 通过选择图示的按钮,可以显示SDK的版本详情࿰…...

利用Python提取将Excel/PDF文件数据
使用Python来创建一个接口,用于接收Excel文件资源链接,下载文件并执行指定的操作,然后返回处理后的数据。以下是一个基本的示例,展示如何使用Flask来创建这样的接口。请注意,这是一个简化的示例,您可能需要…...

纯 CSS 实现瀑布流布局的方法
纯 CSS 实现瀑布流布局的方法 这种方式兼容性不是很好,全部支持需要些时间,但是目前是可以使用 css 写出来的 display: grid; grid-template-columns: repeat(4, 1fr); grid-gap: 10px; grid-template-rows: masonry;全部的 css .container {display:…...

输入法显示到语言栏_状态栏
设置–时间和语言–语言–最右侧"相关设置"中的"拼写、键入和键盘设置" 最下方的"高级键盘设置"–“使用桌面语言栏(如果可用)” 点击"语言栏选项" 接下来就是不同输入法的设置了 搜狗输入法:右键输入法选择"隐藏状态栏"–…...

[samba]同一个文件夹,分不同权限管理
#问题 有一个文件夹A能让用户1拥有写权限,而让用户2拥有只读权限,而用户3啥权限都没有,该如何设置samba呢? #解决办法 1.首先创建一个linux分组(group),命名为samba groupadd samba 2.创建一个samba默认…...