構建現代化的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.pswp.cn/bicheng/93862.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/93862.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/93862.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Apache IoTDB:大數據時代時序數據庫選型的技術突圍與實踐指南

摘要:時序數據庫在大數據時代迎來爆發式增長,IoTDB作為Apache頂級開源項目展現出顯著優勢:1. 性能卓越:支持千萬級數據點/秒寫入,18:1高壓縮比,查詢延遲低至500ms;2. 創新架構:采用樹…

2025年8月16日(星期六):雨騎古蓮村游記

清晨,當第一縷微光還未完全驅散夜幕的靜謐,我們這群由校長領銜的騎行愛好者已整裝待發。咖啡節早市尚未開攤,空氣中彌漫著一種期待與寧靜交織的氛圍,仿佛連時間都在為我們即將開啟的旅程而放慢腳步。今天的目標是古蓮村&#xff0…

Pandas數據預處理中缺失值處理

一、缺失值的概念表現形式1.數據庫中常用null表示2.部分編程語言中用NA表示3.可能表現為空字符串(‘’)或特定數值4.在Pandas中統一用NaN表示(來自NumPy庫,NaN、NAN、nan本質一致)NaN的特性1.與任何值都不相等&#xf…

計算機網絡:(十五)TCP擁塞控制與擁塞控制算法深度剖析

> 當網絡變成"堵城",TCP如何化身智能交通指揮家?揭秘百萬級并發背后的流量控制藝術! ### 一、生死攸關:為什么需要擁塞控制? **真實災難案例**:1986年勞倫斯伯克利實驗室網絡大崩潰,因缺乏擁塞控制導致全網癱瘓36小時。TCP擁塞控制由此誕生,核心解決**資…

python中的單下劃線“_”與雙下劃線“__”的使用場景及“左右雙下劃線”(魔術方法:`__xxx__`)

在Python中,單下劃線“_”和雙下劃線“__”的使用場景和含義有顯著區別,主要體現在命名約定和語法 一、單下劃線“_”的使用場景 單下劃線更多是編程約定(而非強制語法),用于傳遞特定的“暗示”,不影響代碼…

我們為什么需要時序數據庫?

引言在當今數據驅動的世界中,時間序列數據正以前所未有的速度增長。從物聯網設備傳感器、金融交易記錄到應用程序性能監控,時間序列數據無處不在。傳統的關系型數據庫在處理這類數據時往往力不從心,這時時序數據庫(Time Series Database, TSD…

python-林粒粒的視頻筆記1

python的方法和函數指什么 可變類型和不可變類型 不可變類型,比如字符串通過方法調用后,字符串本身的值不改變 要改變需要重新賦值才能進行改變 比如可變數據類型類型,調用方法后可以直接改變原列表 因此,可變數據類型需要再重新賦…

CentOS 7的下載與安裝

一 、CentOS 7的下載與安裝 注意: CentOS 7 已于2024年6月30日停止維護! 1、下載 由于 centos 7 已經停止維護,部分鏡像網站移除了對centos 7的支持,這里找到了部分現在還可以使用的鏡像網站 阿里云開源鏡像站:http…

礦物分類系統開發筆記(二):模型訓練[刪除空缺行]

目錄 一、階段銜接與開發目標 二、數據準備 三、模型選擇與訓練 1. 邏輯回歸(LR) 2. 隨機森林(RF) 3. 高斯樸素貝葉斯(GNB) 4. 支持向量機(SVM) 5. AdaBoost 6. XGBoost 四…

通信方式:命名管道

一、命名管道 1. 命名管道的原理 有了匿名管道,理解命名管道就非常簡單了。 對于普通文件而言,兩個進程打開同一個文件,OS是不會將文件加載兩次的,這兩個進程都會指向同一個文件,那么,也就享有同一份 in…

如何將數據庫快速接入大模型實現智能問數,實現chatbi、dataagent,只需短短幾步,不需要配置工作流!

智能問數系統初始化操作流程 一、系統初始化與管理員賬號創建登錄與初始化提示:首次訪問系統登錄頁,若系統未初始化,會彈出 “系統未完成初始化,請初始化管理員賬號” 提示,點擊【去創建】。填寫管理員信息&#xff1a…

告別手寫文檔!Spring Boot API 文檔終極解決方案:SpringDoc OpenAPI

在前后端分離和微服務盛行的今天,API 文檔是團隊協作的“通用語言”。一份清晰、準確、實時同步的文檔,能極大提升開發和聯調效率。然而,手動編寫和維護 API 文檔(如 Word、Markdown 或 Postman)是一場永無止境的噩夢—…

N4200EX是一款全智能超聲波檢測儀產品簡析

N4200EX是一款全智能超聲波檢測儀,適用于石油、石化、天然氣、氣體生產等行業的壓力管路、閥門、設備的各種防爆場合氣體泄漏、真空泄漏、閥門內漏檢測。●本安防爆設計,防爆、防塵、防水、抗摔。●適應惡劣環境,可在-25℃超低溫環境檢測&…

NestJS @Inject 裝飾器入門教程

一、核心概念解析 1.1 依賴注入(DI)的本質 依賴注入是一種設計模式,通過 IoC(控制反轉)容器管理對象生命周期。在 NestJS 中,Injectable() 標記的類會被容器管理,而 Inject() 用于顯式指定依賴項…

網絡地址詳解

子網劃分詳解:從 IP 地址結構到實際應用 在計算機網絡中,子網劃分是一項關鍵的技術,它能幫助我們更高效地管理 IP 地址資源,優化網絡性能。要深入理解子網劃分,首先需要從 IP 地址的基本結構說起。 一、IPv4 地址的基…

吾日三省吾身 | 周反思 8.19

上周一覽總體來說,上個周是一個被項目驅使而險些喪失自主思考能力的危險階段。相比任何有機械化工作經驗的讀者都有類似的體驗,在手上打螺絲的無盡循環中,自己的腦子就會逐漸喪失對自身的感知以及自主思考的能力。而這個負循環一旦開始&#…

08.19總結

連通性 在無向圖中,若任意兩點間均存在路徑相連,則該圖稱為連通圖。 若刪除圖中任意一個頂點后,剩余圖仍保持連通性,則該圖為點雙連通圖。 若刪除圖中任意一條邊后,圖仍保持連通性,則該圖為邊雙連通圖。 在…

車e估牽頭正式啟動乘用車金融價值評估師編制

8月13日,汽車金融行業職業能力評價規范編制啟動工作會議在廣州圓滿落幕。本次會議由中國機械工業聯合會機械工業人才評價中心主辦,廣州穗圣信息科技有限公司(車e估)承辦。會議匯聚了眾多行業精英,包括中國機械工業聯合…

清空 github 倉庫的歷史提交記錄(創建新分支)

想在 現有倉庫中創建一個新分支 master,刪除原來的 main,然后把 master 重命名為 main,并且清空歷史。可以用下面一條完整的命令序列操作: # 1. 創建一個沒有歷史的新分支 master git checkout --orphan master# 2. 添加當前所有文…

使用B210在Linux下實時處理ETC專用短程通信數據(2)-CPU單核高速數據處理

在上一篇文章中,使用Octave初步驗證了ETC車聯數據的格式。然而,Octave無法實時處理20M的采樣帶寬。我們本節通過C語言,重寫 Octave程序,實現實時處理,涉及下面三個關鍵特點。 文章目錄1. 全靜態內存2. 使用環狀緩存3 無…