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

构建现代化的Web UI自动化测试框架:从图片上传测试实践说起

构建现代化的Web UI自动化测试框架:从图片上传测试实践说起

如何设计一个可维护、可扩展的Web UI自动化测试框架?本文通过一个图片上传测试实例,详细介绍专业测试框架的搭建与实践。

当前测试框架结构

首先,让我们了解一下当前的测试框架结构:

autotests_baidu/
├── conftest.py              # pytest配置文件
├── testwebui/               # Web UI测试目录
│   ├── test_upload.py       # 图片上传测试
├── test_files/              # 测试文件目录
├── pages/                   # 页面对象目录
│   ├── agent_page.py        # 智能体页面对象
│   └── base_page.py         # 基础页面对象
├── data/                    # 测试数据目录
├── config/                  # 配置文件目录
└── requirements.txt         # 依赖包列表

这是一个典型的页面对象模型(Page Object Model)测试框架,具有良好的结构和可扩展性。

原始测试代码分析

原始的上传测试代码虽然功能完整,但有几个可以改进的地方:

# test_upload.py 原始代码
@allure.feature("图片上传测试")
class TestUploadImage:def setup_method(self):self.test_file_path = os.path.abspath("test_files/1.png")assert os.path.exists(self.test_file_path), f"测试文件不存在: {self.test_file_path}"@allure.story("AI头像图片上传")def test_ai_avatar_upload(self, browser):with allure.step("导航到AI头像上传页面"):browser.get("https://www.baidu.com/upload")with allure.step("上传图片文件"):file_input = browser.find_element(By.XPATH,'//input[@name="file" and @type="file"]') #在上传页面查找该元素,一定是input才能上传file_input.send_keys(self.test_file_path)logger.info(f"文件上传成功: {self.test_file_path}")with allure.step("等待处理完成"):import timetime.sleep(20)  # 硬编码等待,需要优化allure.attach(browser.get_screenshot_as_png(),name="上传完成状态",attachment_type=allure.attachment_type.PNG)

优化后的测试框架实现

1. 配置文件 (config/config.py)

import os
from pathlib import Pathclass Config:BASE_DIR = Path(__file__).parent.parentTEST_FILES_DIR = BASE_DIR / "test_files"SCREENSHOTS_DIR = BASE_DIR / "screenshots"REPORTS_DIR = BASE_DIR / "reports"# 浏览器配置BROWSER = "chrome"HEADLESS = FalseIMPLICIT_WAIT = 10# 应用URLBASE_URL = "https://www.baidu.com"UPLOAD_URL = f"{BASE_URL}/upload"# 测试文件路径TEST_IMAGE = TEST_FILES_DIR / "1.png"@classmethoddef setup_directories(cls):"""创建必要的目录"""for directory in [cls.TEST_FILES_DIR, cls.SCREENSHOTS_DIR, cls.REPORTS_DIR]:directory.mkdir(exist_ok=True)Config.setup_directories()

2. 基础页面对象 (pages/base_page.py)

from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.common.exceptions import TimeoutException, NoSuchElementException
import allure
import logginglogger = logging.getLogger(__name__)class BasePage:def __init__(self, driver):self.driver = driverself.wait = WebDriverWait(driver, 10)def find_element(self, by, value, timeout=10):"""查找元素,带显式等待"""try:wait = WebDriverWait(self.driver, timeout)return wait.until(EC.presence_of_element_located((by, value)))except TimeoutException:logger.error(f"元素未找到: {by}={value}")allure.attach(self.driver.get_screenshot_as_png(),name=f"element_not_found_{by}_{value}",attachment_type=allure.attachment_type.PNG)raisedef click(self, by, value):"""点击元素"""element = self.find_element(by, value)element.click()logger.info(f"点击元素: {by}={value}")def input_text(self, by, value, text):"""输入文本"""element = self.find_element(by, value)element.clear()element.send_keys(text)logger.info(f"在元素 {by}={value} 输入文本: {text}")def take_screenshot(self, name):"""截图并附加到Allure报告"""allure.attach(self.driver.get_screenshot_as_png(),name=name,attachment_type=allure.attachment_type.PNG)def wait_for_element_visible(self, by, value, timeout=10):"""等待元素可见"""try:wait = WebDriverWait(self.driver, timeout)return wait.until(EC.visibility_of_element_located((by, value)))except TimeoutException:logger.error(f"元素不可见: {by}={value}")self.take_screenshot(f"element_not_visible_{by}_{value}")raise

3. 上传页面对象 (pages/upload_page.py)

from selenium.webdriver.common.by import By
from .base_page import BasePage
from config.config import Config
import allure
import logginglogger = logging.getLogger(__name__)class UploadPage(BasePage):# 页面元素定位器FILE_INPUT = (By.XPATH, '//input[@name="file" and @type="file"]')UPLOAD_BUTTON = (By.ID, "upload-btn")SUCCESS_MESSAGE = (By.CLASS_NAME, "upload-success")PROGRESS_BAR = (By.ID, "progress-bar")def __init__(self, driver):super().__init__(driver)self.url = Config.UPLOAD_URLdef open(self):"""打开上传页面"""with allure.step("打开上传页面"):self.driver.get(self.url)logger.info(f"打开页面: {self.url}")self.take_screenshot("upload_page_opened")def upload_file(self, file_path):"""上传文件"""with allure.step(f"上传文件: {file_path}"):file_input = self.find_element(*self.FILE_INPUT)file_input.send_keys(str(file_path))logger.info(f"文件上传成功: {file_path}")self.take_screenshot("file_uploaded")def wait_for_upload_complete(self, timeout=30):"""等待上传完成"""with allure.step("等待上传处理完成"):# 等待进度条消失或成功消息出现try:self.wait_for_element_visible(self.SUCCESS_MESSAGE, timeout)logger.info("文件上传处理完成")self.take_screenshot("upload_complete")return Trueexcept TimeoutException:logger.warning("上传处理超时")self.take_screenshot("upload_timeout")return Falsedef is_upload_successful(self):"""检查上传是否成功"""try:success_element = self.find_element(*self.SUCCESS_MESSAGE, timeout=5)return "上传成功" in success_element.textexcept (TimeoutException, NoSuchElementException):return False

4. 优化后的测试用例 (testwebui/test_upload.py)

# -*- coding: utf-8 -*-
import allure
import logging
import pytest
from config.config import Configlogger = logging.getLogger(__name__)@allure.feature("图片上传测试")
class TestUploadImage:"""图片上传测试类"""@pytest.fixture(autouse=True)def setup(self, request):"""测试前置条件"""# 确保测试文件存在assert Config.TEST_IMAGE.exists(), f"测试文件不存在: {Config.TEST_IMAGE}"# 获取页面对象self.upload_page = request.getfixturevalue("upload_page")# 测试后清理def teardown():# 可以添加清理逻辑,如删除上传的文件等passrequest.addfinalizer(teardown)@allure.story("AI头像图片上传 - 成功场景")@allure.severity(allure.severity_level.CRITICAL)def test_ai_avatar_upload_success(self, upload_page):"""测试AI头像图片上传成功场景"""# 打开上传页面upload_page.open()# 上传文件upload_page.upload_file(Config.TEST_IMAGE)# 等待上传完成assert upload_page.wait_for_upload_complete(), "上传处理超时"# 验证上传成功assert upload_page.is_upload_successful(), "上传未成功完成"# 记录成功日志logger.info("AI头像图片上传测试成功")@allure.story("AI头像图片上传 - 大文件测试")@allure.severity(allure.severity_level.NORMAL)def test_ai_avatar_upload_large_file(self, upload_page):"""测试大文件上传处理"""# 实现类似上面但针对大文件的测试pass@allure.story("AI头像图片上传 - 文件格式验证")@allure.severity(allure.severity_level.NORMAL)@pytest.mark.parametrize("file_name,expected", [("1.png", True),("2.jpg", True),("3.gif", True),("4.txt", False),  # 不支持的文件格式])def test_ai_avatar_upload_file_formats(self, upload_page, file_name, expected):"""测试不同文件格式的上传"""file_path = Config.TEST_FILES_DIR / file_nameif file_path.exists():upload_page.open()upload_page.upload_file(file_path)if expected:assert upload_page.wait_for_upload_complete(), f"{file_name} 上传处理超时"assert upload_page.is_upload_successful(), f"{file_name} 上传未成功完成"else:# 验证有错误提示pass

5. 配置文件 (conftest.py)

import pytest
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.firefox.options import Options as FirefoxOptions
from config.config import Config
from pages.upload_page import UploadPage@pytest.fixture(scope="session")
def browser_type():"""返回浏览器类型"""return Config.BROWSER@pytest.fixture(scope="function")
def browser(browser_type):"""初始化浏览器驱动"""driver = Noneif browser_type.lower() == "chrome":options = Options()if Config.HEADLESS:options.add_argument("--headless")options.add_argument("--no-sandbox")options.add_argument("--disable-dev-shm-usage")options.add_argument("--window-size=1920,1080")driver = webdriver.Chrome(options=options)elif browser_type.lower() == "firefox":options = FirefoxOptions()if Config.HEADLESS:options.add_argument("--headless")driver = webdriver.Firefox(options=options)else:raise ValueError(f"不支持的浏览器类型: {browser_type}")# 设置隐式等待driver.implicitly_wait(Config.IMPLICIT_WAIT)yield driver# 测试结束后退出浏览器driver.quit()@pytest.fixture(scope="function")
def upload_page(browser):"""提供上传页面对象"""return UploadPage(browser)

6. 依赖文件 (requirements.txt)

selenium==4.15.0
pytest==7.4.3
allure-pytest==2.13.2
pytest-html==4.0.2
webdriver-manager==4.0.1
openpyxl==3.1.2

关键改进与优势

1. 页面对象模型(POM)设计

  • 将页面操作封装在独立的类中
  • 提高代码复用性和可维护性
  • 降低UI变化对测试代码的影响

2. 配置集中管理

  • 所有配置项集中在Config类中
  • 易于修改和维护
  • 支持多环境配置

3. 智能等待机制

  • 使用显式等待替代硬编码的sleep
  • 提高测试执行效率
  • 更稳定的测试执行

4. 参数化测试

  • 支持多文件格式测试
  • 减少代码重复
  • 提高测试覆盖率

5. 完善的报告机制

  • 丰富的Allure步骤描述
  • 自动截图功能
  • 详细的日志记录

运行测试

# 运行所有测试并生成Allure报告
pytest testwebui/ --alluredir=./reports/allure-results# 生成HTML报告
allure generate ./reports/allure-results -o ./reports/allure-report --clean# 打开报告
allure open ./reports/allure-report

总结

通过本文的介绍,我们看到了如何从一个简单的测试脚本发展成一个完整的、专业的自动化测试框架。这个框架具有以下特点:

  1. 结构清晰:遵循页面对象模型设计模式
  2. 可维护性强:元素定位与业务逻辑分离
  3. 可扩展性好:易于添加新的测试用例和页面
  4. 稳定性高:智能等待机制减少flaky tests
  5. 报告完善:丰富的测试报告和日志

这种框架设计不仅适用于图片上传测试,也可以扩展到其他类型的Web UI自动化测试场景中,为软件质量保障提供强有力的支持。

希望本文对您构建自己的自动化测试框架有所帮助!

http://www.dtcms.com/a/338619.html

相关文章:

  • 【网络运维】Ansible roles:角色管理
  • 前端多环境变量配置全攻略:开发 / 测试 / 生产自动切换实战
  • 时间差值工具Date.now()和performance.now()
  • 【实时Linux实战系列】实时大数据处理与分析
  • 【STM32】HAL库中的实现(五):ADC (模数转换)
  • 服务器经常宕机的原因及解决办法
  • Xftp8传输文件与 Linux 系统 Anaconda 安装
  • 腾讯混元3D系列开源模型:从工业级到移动端的本地部署
  • 游戏相机震动与武器后坐力实现指南
  • 禾赛激光雷达AT128P/海康相机(2):基于欧几里德聚类的激光雷达障碍物检测
  • VScode ROS文件相关配置
  • 知识篇 | 中间件会话保持和会话共享有啥区别?
  • 在Windows高效使用OpenCode的方案
  • Rust 入门 返回值和错误处理 (二十)
  • Docker 快速下载Neo4j 方法记录
  • 管道魔法再现:卡巴斯基发现与CVE-2025-29824漏洞利用相关的进化版后门程序
  • Rust学习笔记(七)|错误处理
  • 人工智能驱动的开发变革
  • 安全多方计算(MPC)技术解析及NssMPClib开源项目实践
  • 驱动开发系列65 - NVIDIA 开源GPU驱动open-gpu-kernel-modules 目录结构
  • ubuntu24 编译安装php-7.4.33
  • Python入门第11课:Python网络请求入门,使用requests库轻松获取网页数据
  • 什么是大数据平台?大数据平台和数据中台有什么关系?
  • RNN如何将文本压缩为256维向量
  • ubuntu下编译c程序报错“ubuntu error: unknown type name ‘uint16_t’”
  • 【保姆级教程~】如何在Ubuntu中装miniconda,并创建conda虚拟环境
  • ubuntu20.04 上 flathub summary exceeded maximum size of 10485760 bytes 的处理
  • 边缘智能体:Go编译在医疗IoT设备端运行轻量AI模型(上)
  • 分布式机器学习之流水线并行GPipe:借助数据并行来实现模型并行计算
  • JVM之Java内存区域与内存溢出异常