当前位置: 首页 > 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集创赛作品扩大影响力,更有丰富电子礼…...

Kandinsky-5.0-I2V-Lite-5s实战:基于Dify平台构建无代码视频生成应用

Kandinsky-5.0-I2V-Lite-5s实战:基于Dify平台构建无代码视频生成应用 1. 引言:让图片动起来的零门槛方案 最近遇到不少朋友在问:有没有什么简单的方法,能让静态图片变成动态视频?传统方案要么需要专业视频编辑技能&a…...

大厂高薪抢手!文科生如何抓住AI时代机遇,实现职业逆袭?

大厂纷纷高薪招聘文科生,引发社会关注。文科生凭借沟通、叙事、逻辑等优势,在大模型理解人类价值观、企业品牌宣传等方面发挥作用。高校也调整专业设置,培养跨学科人才。文章建议文科生根据自身专业,向文案策划、品牌宣传、法务、…...

485总线硬件设计必看:电平匹配、TVS防护,还有exmodbus库快速上手

RS485是工业物联网的标配通信接口。合宙Air780EHV系列Cat.1模组凭借强大外设扩展能力(LCD、摄像头、以太网、CAN等)和LuatOS高效开发环境,支持TCP/MQTT/HTTP/Modbus等主流协议,是工业场景的高性价比之选。 本文聚焦RS485实战&…...

游戏服务器检测扣除消耗防算数溢出的安全判断及解决方法

游戏服务器检测扣除消耗防算数溢出的安全判断及解决方法 数量 > (类型最大值 / 价格) 负数存在风险 价格 > (类型最大值 / 数量) || 价格 < (最小值 / 数量&#xff09; 游戏服务器在处理道具消耗时需防止数值溢出问题。当检测扣除消耗时&#xff0c;应进行双重安全判…...

Python智能内存回收实战:3种GC策略对比+4个生产级调优参数配置(附压测数据)

第一章&#xff1a;Python智能体内存管理策略生产环境部署在高并发、长生命周期的Python智能体服务中&#xff0c;内存管理直接影响系统稳定性与响应延迟。默认的CPython引用计数循环垃圾回收&#xff08;GC&#xff09;机制在动态对象频繁创建销毁的场景下易引发内存抖动和不可…...

(全网最全)分享8款AI工具,毕业论文AIGC率速降至5%!

【CSDN AI底层算法专栏 / 核心摘要】 2026年&#xff0c;学术圈的反AI审查已经演变成了一场“算法级别的军备竞赛”。随着知网、万方全面接入大模型语义探针&#xff0c;靠改同义词、甚至靠传统Prompt洗稿的套路已全线崩溃。为了帮大家避坑&#xff0c;本期专栏我从代码和算法逻…...

Jimeng LoRA环境部署教程:Python+Torch+CUDA兼容性避坑与版本匹配指南

Jimeng LoRA环境部署教程&#xff1a;PythonTorchCUDA兼容性避坑与版本匹配指南 1. 项目简介 Jimeng LoRA&#xff08;即梦LoRA&#xff09;是一个专门为LoRA模型测试设计的轻量级文本生成图像系统。这个项目的核心价值在于它能让你只用加载一次基础模型&#xff0c;然后快速…...

Hunyuan-MT-7B部署教程:Pixel Language Portal与Prometheus监控系统集成

Hunyuan-MT-7B部署教程&#xff1a;Pixel Language Portal与Prometheus监控系统集成 1. 项目概述 Pixel Language Portal是一款基于腾讯Hunyuan-MT-7B大模型构建的创新翻译工具&#xff0c;将传统翻译体验重构为16-bit像素冒险风格。本教程将指导您完成从基础部署到与Prometh…...

VisionPro —— CogImageFileTool图像文件管理实战解析

1. CogImageFileTool核心功能解析 第一次接触CogImageFileTool时&#xff0c;我完全被它强大的图像管理能力震撼到了。这个工具就像工业视觉领域的"智能文件管家"&#xff0c;专门处理图像文件的读写和存储问题。想象一下&#xff0c;你每天要处理上千张生产线上的产…...

SpringBoot集成TTL实现Feign与线程池的TraceId无缝传递(实战优化版)

1. 问题背景与核心挑战 在分布式系统中&#xff0c;日志链路追踪是排查问题的关键手段。想象一下这样的场景&#xff1a;用户请求从网关进入&#xff0c;经过多个微服务处理&#xff0c;每个服务又可能调用其他服务或使用线程池异步处理。当出现问题时&#xff0c;如何快速定位…...