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

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类似于集成学习&#xff0c…...

深入探索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集创赛作品扩大影响力,更有丰富电子礼…...

Linux链表操作全解析

Linux C语言链表深度解析与实战技巧 一、链表基础概念与内核链表优势1.1 为什么使用链表?1.2 Linux 内核链表与用户态链表的区别 二、内核链表结构与宏解析常用宏/函数 三、内核链表的优点四、用户态链表示例五、双向循环链表在内核中的实现优势5.1 插入效率5.2 安全…...

label-studio的使用教程(导入本地路径)

文章目录 1. 准备环境2. 脚本启动2.1 Windows2.2 Linux 3. 安装label-studio机器学习后端3.1 pip安装(推荐)3.2 GitHub仓库安装 4. 后端配置4.1 yolo环境4.2 引入后端模型4.3 修改脚本4.4 启动后端 5. 标注工程5.1 创建工程5.2 配置图片路径5.3 配置工程类型标签5.4 配置模型5.…...

视频字幕质量评估的大规模细粒度基准

大家读完觉得有帮助记得关注和点赞!!! 摘要 视频字幕在文本到视频生成任务中起着至关重要的作用,因为它们的质量直接影响所生成视频的语义连贯性和视觉保真度。尽管大型视觉-语言模型(VLMs)在字幕生成方面…...

Spring Boot面试题精选汇总

🤟致敬读者 🟩感谢阅读🟦笑口常开🟪生日快乐⬛早点睡觉 📘博主相关 🟧博主信息🟨博客首页🟫专栏推荐🟥活动信息 文章目录 Spring Boot面试题精选汇总⚙️ **一、核心概…...

【HTML-16】深入理解HTML中的块元素与行内元素

HTML元素根据其显示特性可以分为两大类:块元素(Block-level Elements)和行内元素(Inline Elements)。理解这两者的区别对于构建良好的网页布局至关重要。本文将全面解析这两种元素的特性、区别以及实际应用场景。 1. 块元素(Block-level Elements) 1.1 基本特性 …...

.Net Framework 4/C# 关键字(非常用,持续更新...)

一、is 关键字 is 关键字用于检查对象是否于给定类型兼容,如果兼容将返回 true,如果不兼容则返回 false,在进行类型转换前,可以先使用 is 关键字判断对象是否与指定类型兼容,如果兼容才进行转换,这样的转换是安全的。 例如有:首先创建一个字符串对象,然后将字符串对象隐…...

微软PowerBI考试 PL300-在 Power BI 中清理、转换和加载数据

微软PowerBI考试 PL300-在 Power BI 中清理、转换和加载数据 Power Query 具有大量专门帮助您清理和准备数据以供分析的功能。 您将了解如何简化复杂模型、更改数据类型、重命名对象和透视数据。 您还将了解如何分析列,以便知晓哪些列包含有价值的数据,…...

JavaScript 数据类型详解

JavaScript 数据类型详解 JavaScript 数据类型分为 原始类型(Primitive) 和 对象类型(Object) 两大类,共 8 种(ES11): 一、原始类型(7种) 1. undefined 定…...

Golang——6、指针和结构体

指针和结构体 1、指针1.1、指针地址和指针类型1.2、指针取值1.3、new和make 2、结构体2.1、type关键字的使用2.2、结构体的定义和初始化2.3、结构体方法和接收者2.4、给任意类型添加方法2.5、结构体的匿名字段2.6、嵌套结构体2.7、嵌套匿名结构体2.8、结构体的继承 3、结构体与…...

代码规范和架构【立芯理论一】(2025.06.08)

1、代码规范的目标 代码简洁精炼、美观,可持续性好高效率高复用,可移植性好高内聚,低耦合没有冗余规范性,代码有规可循,可以看出自己当时的思考过程特殊排版,特殊语法,特殊指令,必须…...