UI自动化测试:Selenium+PO模式+Pytest+Allure整合

news2024/9/29 16:07:57

本人目前工作中未涉及到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

    # 查找一个元素element
    def 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

    # 查找元素elements
    def 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.text
            self.logger.info(f'获取"{model}"元素文本内容为"{text}",元素定位:{loc}')
            return text
        except:
            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_attribute
        except:
            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\WebElement
            WebDriverWait(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. 如果是回到默认的窗口,default
    def 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_handles
                    self.driver.swich_to.window(window_handles[-1])
                else:
                    self.logger.exception('切换失败,没有要切换窗口的信息!!!')
                    self.save_webImgs("切换失败_没有要切换窗口的信息")
                    raise
            elif 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 sleep


class 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 BaiduIndex

driver = 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 初始化完成!")
    yield
    driver.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.title
        assert "哈哈_百度搜索" == web_title

6. 生成Allure测试报告

 Github地址:https://github.com/Zimo6/Selenium_Demo

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

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

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

相关文章

基于Springboot实现点餐平台网站管理系统项目【项目源码+论文说明】

基于Springboot实现点餐平台网站管理系统演示 摘要 随着现在网络的快速发展,网上管理系统也逐渐快速发展起来,网上管理模式很快融入到了许多商家的之中,随之就产生了“点餐平台网站”,这样就让点餐平台网站更加方便简单。 对于本…

设计模式 - 行为型模式考点篇:观察者模式(概述 | 案例实现 | 优缺点 | 使用场景)

目录 一、行为型模式 1.1、观察者模式 1.1.1、概述 1.1.2、案例实现 1.1.3、优缺点 1.1.4、使用场景 一、行为型模式 1.1、观察者模式 1.1.1、概述 观察者模式又称为 发布 - 订阅 模式.,就是让多个观察者对象同时监听同一个主题对象. 这个主题对象在变化的同…

详解一典型的反激式开关电源方案

理解一个单端反激式开关电源方案: 1、抛出问题: 如图,在某系统方案上看到下图所示的单端反激式开关电源方案。 2、解析问题: 2.1、乍一看: 典型的AC-DC电路,考虑了安规及过压过流保护,如&am…

黑马点评-06缓存雪崩问题(大量key失效)及其解决方案

缓存雪崩问题(大量key失效) 解决方案 缓存雪崩是指在同一时段大量的缓存key同时失效或者Redis服务宕机,导致大量请求到达数据库给服务器带来巨大压力 当我们批量导入缓存数据的时候可以给不同的Key的TTL添加随机值,让其在不同时间段分批失效利用Redis集群提高服务的可用性 使…

用低代码轻松实现高效、易用的流程审批表单

在企业运营中,流程引擎表单的配置和使用是一个重要的环节。无论是发起人、审批人还是其他角色,他们在流程中所看到的表单内容可能会有所不同,因此我们需要根据不同的场景灵活选择和设计表单。每一个流程节点都需要一个表单,通过灵…

基于BES平台音乐信号处理之DRC算法实现

基于BES平台音乐信号处理之DRC算法实现 是否需要申请加入数字音频系统研究开发交流答疑群(课题组)?加我微信hezkz17, 本群提供音频技术答疑服务 1 DRC实现 drc.h 2 调用 audio_process.c 3 DRC动态范围控制算法在音乐信号处理中的位置 4 DRC具体细节源码 可参考…

C++QT-day4

#include <iostream> //运算符重载 using namespace std;class Person { // //全局函数实现运算符重载 // friend const Person operator(const Person &L,const Person &R); // //全局函数实现-运算符重载 // friend const Person operator-(const …

【LeetCode】——链式二叉树经典OJ题详解

主页点击直达&#xff1a;个人主页 我的小仓库&#xff1a;代码仓库 C语言偷着笑&#xff1a;C语言专栏 数据结构挨打小记&#xff1a;初阶数据结构专栏 Linux被操作记&#xff1a;Linux专栏 LeetCode刷题掉发记&#xff1a;LeetCode刷题 算法头疼记&#xff1a;算法专栏…

PTE考试预览

目录 pte评分机制是对比 不同水平的人之后跟你匹配 前两次 两个听力题没有写 咯噔一下 无源65分 Headset Check 调试耳机听筒 Microphone zhigeiCheck 调试麦克风 麦克风测试话语 老师说她放在鼻梁骨那里声音很好 自我介绍 读完之后 &#xff0c;立马点next&#xf…

PostgreSQL安装错误:Problem running post-install step

问题描述 安装包&#xff1a;pgpostgresql-14.9-1-windows-x64 postgresql-16.0-1-windows-x64 采取措施 一、 首先安装的是16版本的程序&#xff0c;报错后卸载尝试安装14版本软件&#xff0c;依旧报错。 二、 网上搜索&#xff0c;发现该博客&#xff1a; PostgreSQL安…

基于YOLOv8模型的足球目标检测系统(PyTorch+Pyside6+YOLOv8模型)

摘要&#xff1a;基于YOLOv8模型的足球目标检测系统可用于日常生活中检测与定位足球目标&#xff0c;利用深度学习算法可实现图片、视频、摄像头等方式的目标检测&#xff0c;另外本系统还支持图片、视频等格式的结果可视化与结果导出。本系统采用YOLOv8目标检测算法训练数据集…

.NET与C#未来大流行是必然趋势

python性能差、应用范围有限&#xff0c;虽然目前很流行 C门槛高 C语言只适合底层 java大势已去 c#似乎是综合实力最强的语言&#xff0c;而且开发调试都很简单&#xff0c;适合很多行业&#xff0c;开发成本低。

Web服务器概述及http协议

Web Server&#xff08;网页服务器&#xff09; 一个 Web Server 就是一个服务器软件&#xff08;程序&#xff09;&#xff0c;或者是运行这个服务器软件的硬件&#xff08;计算机&#xff09;。其主要功能是通过 HTTP 协议与客户端&#xff08;通常是浏览器&#xff08;Brow…

远程发送剪切板,屏幕截图

使用场景 线上答题时一般有录屏&#xff0c;局域网内可发送内容到电脑上剪切板&#xff0c;电脑上直接CtrlV双机位看不到屏幕是可以远程截屏 下载 访问地址下载程序&#xff1a;https://gitee.com/guzhengchang/shared-clipboard/releases/tag/v0.2 双击前面下载的程序&am…

牛津大学海外学习:14天的知识与文化之旅

牛津——一个充满学术氛围与古老传统的城市&#xff0c;对于我这次14天的海外学习经验来说&#xff0c;这里每一个角落都隐藏着知识和历史的故事。作为中国的一名学生&#xff0c;能够在这里学习、生活&#xff0c;真是一次难得的机会。 我报名的是《人工智能》课程&#xff0…

嵌入式Linux裸机开发(五)中断管理

系列文章目录 文章目录 系列文章目录前言STM32 中断系统IMX6U中断控制8个中断GIC中断控制器GIC介绍中断IDGIC逻辑分块GIC协处理器 中断使能中断优先级 重点代码分析官方SDK函数start.S文件自行编写中断驱动文件 前言 最近在学习中发现&#xff0c;学Linux嵌入式不仅是对Linux的…

大型组网如何使用BFD提高可靠性

1.当前现网业务存在哪些问题&#xff1f; 2.BFD的工作机制讲解 3.BFD联动静态路由以及OSPF实战 --- BFD - 双向转发检测 - 通用标准 SLA --- 介质无关 协议无关 --- BFD基本原理 --- BFD应用场景配置方式 --- BFD结合OSPF --- BFD结合静态路由 跨网段…

MySQL 面试知识脑图 初高级知识点

脑图下载地址&#xff1a;https://mm.edrawsoft.cn/mobile-share/index.html?uuid18b10870122586-src&share_type1 sql_mode 基本语法及校验规则 ONLY_FULL_GROUP_BY 对于GROUP BY聚合操作&#xff0c;如果在SELECT中的列&#xff0c;没有在GROUP BY中出现&#xff…