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

如果我的博客对你有帮助、如果你喜欢我的博客内容,请 “点赞” “评论” “收藏” 一键三连哦! 

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

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

相关文章

【ElasticSearch】更新es索引生命周期策略,策略何时对索引生效

大家好,我是好学的小师弟,今天和大家讨论下更新es索引生命周期策略后,策略何时对索引生效 结论: 若当前索引已应用策略A(旧),更新完策略A后,新的策略A会立即对原来的已经应用该策略的索引生效;若当前索引…

OPPO realme 真我 一加 刷机工具下载 ColorOS Upgrade Tool

Download Realme Upgrade Tool for Windows Download ColorOS Upgrade Tool for Windows Realme升级工具是由Realme开发的Windows应用程序,可帮助用户升级其Realme设备上的固件。此工具支持在Realme Ul 3.0或更高版本上运行的Realme 设备。OPPO realme 真我 一加 …

GienTech动态|入选软件和信息技术服务竞争力百强;参展世界计算大会、网络安全博览会

———— GienTech动态 ———— 中电金信参展广东省网络安全博览会、世界计算机大会 近期,中电金信跟随中国电子参展2023年广东省网络安全博览会(下简称“博览会”)和2023世界计算大会。在两大峰会上,中电金信展出了金融级数字底…

【虹科干货】Redis Enterprise 自动分层技术:大数据集高性能解决方案

越来越多的应用程序依赖于庞大的数据集合,而这些应用程序必须快速响应。借助自动分层,Redis Enterprise 7.2 帮助开发人员轻松创建超快的应用程序。何乐而不为? Redis将数据存储在内存中,因此应用程序能以最快的速度检索和处理数…

“过度炒作”的大模型巨亏,Copilot每月收10刀,倒赔20刀

大模型无论是训练还是使用,都比较“烧钱”,只是其背后的成本究竟高到何处?已经推出大模型商用产品的公司到底有没有赚到钱?事实上,即使微软、亚马逊、Adobe 这些大厂,距离盈利之路还有很远!同时…

精品Python的计算机科学研究话题管理系统-可视化大屏

《[含文档PPT源码等]精品Python的计算机科学研究话题管理系统设计与实现-爬虫》该项目含有源码、文档、PPT、配套开发软件、软件安装教程、项目发布教程等! 软件开发环境及开发工具: 开发语言:python 使用框架:Django 前端技术…

Vue 中 KeepAlive 内置缓存使用

KeepAlive 介绍及使用场景 KeepAlive 是 vue 中的内置组件&#xff0c;当多个组件动态切换时可以对实例状态进行缓存&#xff0c;用法如下 <router-view v-slot"{ Component }"><keep-alive><component :is"Component" /></keep-al…

让 Visual Studio 用上 ChatGPT

一、简介 Visual chatGPT Studio 是 Visual Studio 的一个免费扩展&#xff0c;它直接在 IDE 中添加了 chatGPT 功能。它允许用户以可以根据菜单功能的方式使用 chatGPT。 二、功能介绍 该扩展提供了一组使用 ChatGPT 命令&#xff0c;可以在编辑器中选择你需要处理的代码或…

火伞云Web应用防火墙的特点与优势

在前文中&#xff0c;我们已经介绍了Web应用防火墙&#xff08;WAF&#xff09;的基本原理和重要性。接下来&#xff0c;我们将深入探讨火伞云Web应用防火墙的特点与优势&#xff0c;了解它如何为企业提供更为完善和专业的网络安全保障。 一、强大的防御能力 火伞云Web应用防火…

SSM - Springboot - MyBatis-Plus 全栈体系(二十六)

第六章 SpringBoot 快速启动框架&#xff1a;SpringBoot3 实战 一、SpringBoot3 介绍 1. SpringBoot3 简介 SpringBoot 版本&#xff1a;3.0.5 到目前为止已经学习了多种配置 Spring 程序的方式。但是无论使用 XML、注解、Java 配置类还是他们的混合用法&#xff0c;都会觉…

Git版本控制管理

Git基础_环境配置 当安装Git后首先要做的事情是设置用户名称和email地址。这是非常重要的&#xff0c;因为每次Git提交都会使用该用户信息。 设置用户信息 git config --global user.name "Bandits" git config --global user.email "gb010704163.com"查…

【安全】linux audit审计使用入门

文章目录 1 audit简介2 auditctl的使用2 audit配置和规则3 工作原理4 audit接口调用4.1 获取和修改配置4.2 获取和修改规则4.3 获取审计日志 5 audit存在的问题5.1 内核版本5.2 审计日志过多造成的缓存队列和磁盘问题5.2 容器环境下同一个命令的日志存在差异 6 参考文档 1 audi…

【算法|双指针系列No.6】leetcode LCR 179. 查找总价格为目标值的两个商品

个人主页&#xff1a;兜里有颗棉花糖 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 兜里有颗棉花糖 原创 收录于专栏【手撕算法系列专栏】【LeetCode】 &#x1f354;本专栏旨在提高自己算法能力的同时&#xff0c;记录一下自己的学习过程&#xff0c;希望…

Rule-Engine-Starter V1.0.0

一个轻量级的规则引擎、搜索引擎&#xff0c;让条件匹配简单、优雅。 GIT地址 https://gitcode.cosmoplat.com/15011240224/rule-engine-starter 介绍 Rule-Engine-Starter 是一个轻量级规则引擎&#xff0c;V1.0.0主要解决条件匹配问题。比如飞书文档&#xff0c;每个文档都…

电脑系统升级Win11变卡顿?如何解决!

关于Win11系统更新的相关问题&#xff0c;Win11每一次更新之后会给大家带来更多功能&#xff01;但是每次更新也可能会出现掉驱动、无法打开应用、甚至是蓝屏或者进不去主页等问题&#xff01;所以为了帮助大家预防这些更新出现的后遗症&#xff0c;分享了一些小的方法&#xf…

[Error]在Swift项目Build Settings的Preprocessor Macros中定义的宏无效的问题

问题 如图&#xff0c;在Build Settings -> Preprocessor Macros中添加了ISADEMO1。但在代码中判断无效&#xff0c;还是会输出“isn’t ADemo” #if ISADEMOprint("is ADemo") #elseprint("isnt ADemo") #endif解决 如图&#xff0c;要让Preproces…

【已解决】MySQL:执行sql查询出错误数据(MySQL隐藏机制-类型转换导致)

目录 问题现象&#xff1a; 问题分析&#xff1a; 结论&#xff1a; 解决方法&#xff1a; 拓展&#xff1a; 问题现象&#xff1a; 今天在项目开发中发现了一个非常奇怪的bug&#xff1a; 如图&#xff0c;我在数据库中以“dept_id 1712651046956421123”为条件&#xff…

WEEX编译|加密市场三季度回顾及未来展望

作者&#xff1a;Greg Cipolaro&#xff0c;NYDIG 全球研究主管 编译&#xff1a;WEEX 唯客交易所 本文要点&#xff1a; ● 在充满挑战的第三季度&#xff0c;比特币价格下跌 11.1%&#xff0c;因为众多资产类别都在努力应对利率上升的影响和对经济衰退的担忧。 ● 比特币…

CentOS 7 基于C 连接ZooKeeper 客户端

前提条件&#xff1a;CentOS 7 编译ZooKeeper 客户端&#xff0c;请参考&#xff1a;CentOS 7 编译ZooKeeper 客户端 1、Docker 安装ZooKeeper # docker 获取zookeeper 最新版本 docker pull zookeeper# docker 容器包含镜像查看 docker iamges# 准备zookeeper 镜像文件挂载对…

什么是指标体系,怎么搭建一套完整的指标体系?(附PDF素材)

什么是指标体系&#xff0c;怎么搭建一套完整的指标体系&#xff1f;数字化转型过程中&#xff0c;这个问题一直困扰着数据分析师。主要体现在&#xff1a; 各部门根据业务需求&#xff0c;都有一部分量化指标&#xff0c;但不够全面&#xff0c;对企业整体数据分析应用能力提…