从〇 搭建PO模式的Web UI自动化测试框架

news2024/12/22 22:18:51

Page Object模式简介

核心思想

将页面元素和操作行为封装在独立的类中,形成页面对象(Page Object)。每个页面对象代表应用程序中的一个特定页面或组件。

优点:

代码复用性高

页面对象可以在多个测试用例中复用。

易于维护

如果页面布局或元素发生变化,只需要修改对应的页面对象类,而不需要修改所有相关的测试用例。


项目结构介绍

在这里插入图片描述

allure-results/:

  • 作用: 存放 Allure 生成的测试结果文件。Allure 从这里读取数据来生成报告。
    内容示例: 包含测试结果的 JSON 文件、截图等附件。

pages/:

  • 作用:存放Page Object类。Page Object类将页面元素及其操作封装起来,每个类对应一个页面或组件。这种封装使得测试代码更为简洁易读,并减少了页面变更时的修改量。

  • 示例:

    • base_page.py: 定义了一个基础的Page类,提供了页面操作的通用方法,如查找元素、点击、输入文本等。其他页面类都会继承这个类。
    import allure
    from selenium.common import TimeoutException
    from selenium.webdriver.remote.webdriver import WebDriver
    from selenium.webdriver.support.ui import WebDriverWait
    from selenium.webdriver.support import expected_conditions as EC
    
    
    class BasePage:
        def __init__(self, driver: WebDriver):
            self.driver = driver
            self.timeout = 5
    
        def find_element(self, locator):
            """
            获取元素的手柄
            :param locator:指定元素的定位器
            :return:元素手柄
            """
            # presence_of_element_located:等待某个元素出现在 DOM 中
            # visibility_of_element_located:等待某个元素不仅存在于 DOM 中,而且是可见的。
            try:
                # 尝试等待元素的可见性
                return WebDriverWait(self.driver, self.timeout).until(EC.visibility_of_element_located(locator))
            except TimeoutException:
                # 如果元素的可见性等待超时,继续等待元素的存在
                return WebDriverWait(self.driver, self.timeout).until(EC.presence_of_element_located(locator))
    
        def element_exists(self, locator):
            """
            用显示等待判断指定元素是否存在
            :param locator:待判断元素的定位器
            :return:True/False
            """
            try:
                self.find_element(locator)
                return True
            except TimeoutException:
                return False
    
        def upload_file(self, file_path, locator):
            """
            公共的上传文件函数
            :param file_path (str):要上传的文件的路径
            :param locator (tuple):上传元素的定位器
            :return: 无
            """
            with allure.step(f"上传文件:{file_path}"):
                upload_element = self.find_element(locator)
                upload_element.send_keys(file_path)
    
        def get_text(self, locator):
            text_content = self.find_element(locator).text
            return text_content
    
        def click(self, locator):
            """
            点击指定的元素
            :param locator: 指定元素的定位器
            :return:
            """
            with allure.step(f"点击指定元素:{locator}"):
                self.find_element(locator).click()
    
        def double_click(self, locator):
            """
            点击指定的元素
            :param locator: 指定元素的定位器
            :return:
            """
            with allure.step(f"双击指定元素:{locator}"):
                self.find_element(locator).double_click()
    
        def context_click(self, locator):
            """
            右键指定的元素
            :param locator: 指定元素的定位器
            :return:
            """
            with allure.step(f"右键指定元素:{locator}"):
                self.find_element(locator).context_click()
    
        def send_keys(self, locator, text):
            """
            收入数据
            :param locator: 指定元素的定位器
            :param text: 待输入的数据
            :return:
            """
            with allure.step(f"在{locator}中输入:{text}"):
                self.find_element(locator).send_keys(text)
    
        def get_title(self):
            """
            获取当前页面表头
            :return:
            """
            return self.driver.title
    
    
    • baidu_page.py: 这个文件是一个具体的Page Object类的示例,封装了登录页面的操作。通常会包含元素定位和一些基本的操作方法,如输入输入、点击搜索按钮等。
    import time
    import allure
    from selenium.webdriver.common.by import By
    from .base_page import BasePage
    
    
    class LoginPage(BasePage):
        # 元素:element
        # 定位器:positioner
        ep_INPUT = (By.XPATH, '//*[@id="kw"]')
        ep_SEARCH_BUTTON = (By.XPATH, '//*[@id="su"]')
        ep_ASSERT_TEXT = (By.XPATH, '//*[@id="1"]/div/section/div[1]/span')
        ep_INPUT_IMAGE = (By.XPATH, '//*[@id="form"]/span[1]/span[1]')
        ep_SELECT_FILE = (By.XPATH, '//*[@id="form"]/div/div[2]/div[2]/input')
        ep_IMAGE_PATH = 'D:\\UITestProject\\20240815175707543.jpg'
        ep_ASSERT_IMAGE = (By.XPATH, '//*[@id="app"]/div/div[1]/div/div[1]/div/div/div[2]/div[1]')
    
        # @allure.step("输入搜素text") # 用于记录测试用例中的具体步骤
        def input_value(self, value):
            """
            输入搜索值
            :param value:
            :return:
            """
            self.send_keys(self.ep_INPUT, value)
    
        # @allure.step("点击搜索按钮") # 用于记录测试用例中的具体步骤
        def click_search(self):
            """
            点击搜索按钮
            :return:
            """
            self.click(self.ep_SEARCH_BUTTON)
    
        # @allure.step("获取判断元素的text")
        def get_assert_text_text(self):
            return self.get_text(self.ep_ASSERT_TEXT)
    
        # @allure.step("上传图片")  # 用于记录测试用例中的具体步骤
        def upload_image(self):
            self.click(self.ep_INPUT_IMAGE)
            self.upload_file(self.ep_IMAGE_PATH, self.ep_SELECT_FILE)
    
        def get_assert_image_text(self):
            print(self.get_text(self.ep_ASSERT_IMAGE))
            return self.get_text(self.ep_ASSERT_IMAGE)
    
    

tests/:

  • 作用: 存放所有的测试用例文件。每个测试文件通常以 test_ 开头,包含测试函数和测试类。
    • 示例:
      • test_baidu.py:包含针对百度功能的测试用例。
    import allure
    import pytest
    from pages.baidu_page import LoginPage
    
    
    # @pytest.mark.usefixtures("setup") # 在测试用例之前和之后执行
    @allure.feature("百度搜索功能")
    class TestLogin:
        # @pytest.mark.skip(reason="跳过该测试用例!") # 跳过该测试用例!
        # @pytest.mark.skipif(sys.platform == "win32", reason="Windows上不运行该用例") # 根据条件判断是否跳过该测试用例
        # @pytest.mark.run(order=1) # 控制运行顺序【优先级】,从小到大依次执行
        @allure.title("搜索文本功能")  # 用于给测试用例设置一个自定义的标题,替代默认的函数名称。
        @pytest.mark.parametrize("value, text", [("猪猪", "其他人还搜"), ("狗子", "相关动物")])  # 参数化
        def test_search_text(self, value, text):
            login_page = LoginPage(self.driver)
            login_page.input_value(value)
            login_page.click_search()
            assert login_page.get_assert_text_text() == text
            assert login_page.get_title() == f"{value}_百度搜索"
    
        @allure.title("搜索图片功能")  # 用于给测试用例设置一个自定义的标题,替代默认的函数名称。
        @allure.description("测试是否可以搜索出与上传图片有关的内容")  # 为测试用例添加详细描述,帮助更好地理解测试的目的和背景。
        def test_search_image(self):
            login_page = LoginPage(self.driver)
            login_page.upload_image()
            assert login_page.get_assert_image_text() == "文字提取"
    		
    

utils/:

  • 作用:作用: 存放工具类和辅助功能的脚本。通常包括 WebDriver 的工厂类、常用的功能函数等。
  • 示例:
    • driver_factory.py:创建和管理 WebDriver 实例的工厂类。
from selenium import webdriver

def get_driver(browser_name="chrome"):
    # 判断启动的浏览器,不区分大小写
    if browser_name.lower() == "chrome":
        options = webdriver.ChromeOptions()
        # 添加启动时最大化窗口的参数
        options.add_argument("--start-maximized")
        return webdriver.Chrome(options=options)

    elif browser_name.lower() == "firefox":
        options = webdriver.FirefoxOptions()
        options.add_argument("--start-maximized")
        return webdriver.Firefox(options=options)

    elif browser_name.lower() == "edge":
        options = webdriver.EdgeOptions()
        options.add_argument("--start-maximized")
        return webdriver.Edge(options=options)

    else:
        raise ValueError(f"不支持该浏览器: {browser_name}")
			

conftest.py:

  • 作用:用于设置和清理测试环境、初始化 WebDriver 实例等。

    import allure
    import pytest
    from utils.driver_factory import get_driver
    
    
    # 在每个测试类之前执行一次,需要手动添加注解后才能生效
    @pytest.fixture(scope="class")
    def setup(request):
        # 使用 get_driver 函数创建一个 WebDriver,可以根据需求修改浏览器类型
        # driver = get_driver(browser_name="chrome")
        driver = get_driver(browser_name="edge")
        # 将创建的 WebDriver 实例赋值给测试类,以便测试用例可以使用
        request.cls.driver = driver
        # yield 语句之前的代码是测试前置条件,yield 之后的代码是测试后置条件
        yield
        driver.quit()
    
    
    # 在每个测试用例之前执行一次,不需要额外给测试用例添加注解
    @pytest.fixture(autouse=True)
    def test_case_setup(request):
        # 将创建的 WebDriver 实例赋值给测试类,以便测试用例可以使用
        driver = get_driver(browser_name="chrome")
        request.cls.driver = driver
        # 打开指定链接
        driver.get("https://www.baidu.com")
        # yield 语句之前的代码是测试前置条件,yield 之后的代码是测试后置条件
        yield
        # 在测试用例执行完毕后,关闭浏览器并退出 WebDriver
        driver.close()
    
    
    # 生成测试报告的钩子函数
    @pytest.hookimpl(tryfirst=True, hookwrapper=True)
    def pytest_runtest_makereport(item, call):
        # 先执行测试
        outcome = yield
        report = outcome.get_result()
    
        # 如果测试失败并且是一个 "call" 状态(表示实际运行测试代码时出错)
        # if report.when == "call" and report.failed:
        # 测试通过或不通过都截图
        if report.when == "call":
            # 通过 request 对象获取 driver 实例
            driver = item.funcargs['request'].cls.driver
            # 截图并附加到 Allure 报告中
            try:
                allure.attach(driver.get_screenshot_as_png(),
                              name="测试结果截图",
                              attachment_type=allure.attachment_type.PNG)
            except Exception as e:
                # 将错误日志作为附件添加到 Allure 报告中
                allure.attach(f"截图失败: {e}",
                              name="截图失败日志",
                              attachment_type=allure.attachment_type.TEXT)
    
    

pytest.ini:

  • 作用: 配置 pytest 的行为,包括指定测试目录、文件名模式、标记等。
[pytest]
# --continue-on-collection-errors 测试用例报错后,仍然继续运行
# --alluredir=./allure-results:指定生成的测试数据存储的位置
addopts = --continue-on-collection-errors --alluredir=./allure-results

# 指定 pytest 搜索测试用例的根目录
# 所有测试文件和目录都会在 `tests` 目录下被自动发现和执行
testpaths = tests
# 指定测试文件的命名模式
# pytest 只会识别文件名以 `test_` 开头的 Python 文件作为测试文件
python_files = test_*.py

# 指定测试类的命名模式
# pytest 只会识别以 `Test` 开头的类名作为测试类
python_classes = Test*

# 指定测试函数的命名模式
# pytest 只会识别以 `test_` 开头的函数名作为测试函数
python_functions = test_*
	

requirements.txt

  • 作用:当前项目中所使用到的第三方库

    allure-pytest==2.13.5
    allure-python-commons==2.13.5
    attrs==24.2.0
    certifi==2024.7.4
    cffi==1.17.0
    colorama==0.4.6
    exceptiongroup==1.2.2
    h11==0.14.0
    idna==3.7
    iniconfig==2.0.0
    outcome==1.3.0.post0
    packaging==24.1
    pluggy==1.5.0
    pycparser==2.22
    PySocks==1.7.1
    pytest==8.3.2
    selenium==4.23.1
    sniffio==1.3.1
    sortedcontainers==2.4.0
    tomli==2.0.1
    trio==0.26.2
    trio-websocket==0.11.1
    typing_extensions==4.12.2
    urllib3==2.2.2
    websocket-client==1.8.0
    wsproto==1.2.0
    
    

运行

运行测试:

pytest

查看Allure测试报告

allure serve allure-results

测试报告示例

在这里插入图片描述

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

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

相关文章

10 个最佳 Java NLP 库和工具

发现用于高级自然语言处理的最佳 Java NLP 库。通过文本分析、情感分析等增强您的应用程序。 Java 已成为一种功能强大且用途广泛的编程语言,广泛用于开发跨领域的各种应用程序。其丰富的库和工具生态系统使其成为各种任务的理想选择,包括自然语言处理 (…

NVDLA专题1:NVDLA框架介绍

NVDLA概述 深度学习的计算部分主要可以分为4部分:卷积、激活单元(神经元)、池化和归一化。由于每个运算模块都有比较独特的共享特征,因此非常适合给每个模块设计一个对应的特殊硬件实现:内存访问模式容易预测并且很容…

超高速NVME FPGA存储卡记录

板卡概述 XNM-KU-M4 是一款基于KU115 的高速存储模块。 该模块基于NVME固态硬盘,主要用于高速实时数据流的存储和回放,主要用于雷达、通信、电子、卫星等领域,包括高速ADC数据采样实时记录、DAC数据回放、基于光纤或者Rapid IO的高速数据记录…

SOLIDWORKS 2024:开启创新设计新篇章

随着2024年的到来,SOLIDWORKS也迎来了全新的篇章——SOLIDWORKS 2024。这款由Dassault Systmes开发的三维CAD软件,一直以其强大的功能和易用性引领着工程设计领域的潮流。作为SOLIDWORKS在中国的官方授权代理商,亿达四方致力于为企业提供最新…

一个人活成一个团队:python的django项目devops实战

文章目录 一、需求规划二、代码管理三、创建流水线1、配置流水线源 四、自动测试五、自动构建六、自动部署七、总结 对于开发团队来说提高软件交付的速度和质量是一个永恒的话题,对于个人开发者来说同样如此。作为一个码农,一定会有几个自己私有的小项目…

漏洞扫描的重要性,如何做好漏洞扫描服务

随着互联网技术的飞速发展,网络安全问题已成为不容忽视的重大挑战。其中,系统漏洞威胁作为最常见且严重的安全危险之一,对组织和个人的信息资产构成了巨大威胁。下面我们就来了解下漏洞扫描的好处、漏洞扫描的操作方法以及如何做好网络安全。…

【学习笔记】A2X通信的协议(九)- 广播远程ID(BRID)

3GPP TS 24.577 V18.1.0的技术规范,主要定义了5G系统中A2X通信的协议方面,特别是在PC5接口和Uu接口上的A2X服务。以下是文件的核心内容分析: 7. 广播远程ID(BRID) 7.1 概述 本条款描述了以下程序: 在用…

复现、并改进open-mmlab的mmpose详细细节

复现open-mmlab的mmpose详细细节 1.配置环境2.数据处理3.训练4.改进mmpose4.1 快速调试技巧4.2 快速定位4.3 改进backbone4.3.1 使用说明4.3.2 改进案例4.3.2.1 复现mmpose原配置文件4.3.2.2 复现开源项目4.3.2.3 修改配置文件4.3.2.4 修改新模型 4.4 添加auxiliary_head4.4.1 …

Python OpenCV 影像处理:读取、显示、储存影片

► 前言 本篇将介绍使用OpenCV Python撷取网路摄影机(webcam)的即时画面影像处理与显示,以及透过读取、显示和储存硬盘中的影片档案来实现影片操作。这将帮助大家了解如何使用OpenCV在影片上进行各种操作。 ► OpenCV Python撷取网路摄影机 OpenCV首先建立了一个…

【计算机网络】TCP实战

其实有了UDP的基础,TCP不管怎么说学习起来都还是比较舒服的,至少是比直接就学习TCP的感觉好。 这篇文章最多就是介绍一下起手式,如果想带业务的话和UDP那篇是完全一样的,就不进行演示了。 总的来说还是很简单的。 目录 Echo服务端…

魔方远程时时获取短信内容APP 前端Vue 后端Ruoyi框架(含搭建教程)

前端Vue 后端Ruoyi框架 APP原生JAVA 全兼容至Android14(鸿蒙 澎湃等等) 前后端功能: ①后端可查看用户在线状态(归属地IP) ②发送短信(自定义输入收信号码以及短信内容,带发送记录) ③短信内容分类清晰(接收时间、上传时间等等) ④前后端分离以及A…

Doris与StarRocks

目录 Doris Doris 架构 存储引擎 查询引擎 索引结构 存储模型 物化视图 使用场景 StarRocks 架构设计 架构选择 存算一体 节点 FE BE 存算分离 节点 存储 缓存 适用场景 OLAP 多维分析 实时数据仓库 高并发查询 统一分析 Doris和StarRocks对比 大规模…

Vue3中组件的多种写法

SFC单文件组件,一个vue写一个组件 使用 defineComponent h函数 去进行组件编写 使用 defineComponent JSX/TSX 去进行组件编写 需要安装插件pnpm i vitejs/plugin-vue-jsx -D 引入 配置 使用组件

Android的OkHttp使用和原理

前言 OkHttp的出现代替了HttpUrlConnection,被谷歌官方收纳为底层的网络框架。特点如下: 支持HTTP/2框架下的socket复用通过连接池减少连接的延时使用GZIP进行数据压缩使用缓存技术避免重复请求 当网络出现问题时,OkHttp会静默重新恢复连接…

uniapp组件使用

uni-popup 默认z-index是99 https://uniapp.dcloud.net.cn/component/uniui/uni-popup.html#uni-popup-%E5%BC%B9%E5%87%BA%E5%B1%82 uni-icons uniapp自带图标&#xff1a;https://hellouniapp.dcloud.net.cn/pages/extUI/icons/icons <uni-icons type"left"…

基于JAVA的在线教育系统设计与实现,源码、部署+讲解

摘 要 随着信息化的日益发展&#xff0c;互联网信息技术的发展日新月异。互联网在线教育模式也在不断的被革新。从传统的线下辅导授课&#xff0c;转变成现在的线上教育遍地开花。线上教育已经犹如雨后春笋一般冒芽而出&#xff0c;这为我们的生活带来了许多变动。 基于网络…

江协科技STM32学习笔记(第12章 PWR电源控制)

第12章 PWR电源控制 12.1 PWR电源控制 12.1.1 PWR简介 芯片在3种低功耗模式下&#xff0c;是没法直接再下载程序的。这是因为芯片在睡眠&#xff0c;不会关注调试端口了。解决办法就是&#xff1a;1.按住复位键不动&#xff1b;2.点下载按钮&#xff1b;3.及时从开复位键。这…

怎样使用sudo的时候不需要输入密码?

在Ubuntu等Linux系统下&#xff0c;经常要在个人账户使用sudo命令来执行一些需要root权限的命令&#xff0c;但是需要输入该账户的密码&#xff0c;有时候显得很繁琐&#xff0c; 那么怎样使用sudo的时候不需要输入密码呢&#xff1f; 有如下两种方法&#xff1a; 常规方法1…

颠覆传统 北大新型MoM架构挑战Transformer模型,显著提升计算效率

挑战传统的Transformer模型设计 在深度学习和自然语言处理领域&#xff0c;Transformer模型已经成为一种标准的架构&#xff0c;广泛应用于各种任务中。传统的Transformer模型依赖于一个固定的、按深度排序的层次结构&#xff0c;每一层的输出都作为下一层的输入。这种设计虽然…

Spring Boot集成selenium实现自动化测试

1.什么是selenium&#xff1f; Selenium 是支持web 浏览器自动化的一系列工具和 库的综合项目。 它提供了扩展来模拟用户与浏览器的交互&#xff0c;用于扩展浏览器分配的分发 服务器&#xff0c; 以及用于实现W3C WebDriver 规范 的基础结构&#xff0c; 该规范允许您为所有主…