構建現代化的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
總結
通過本文的介紹,我們看到了如何從一個簡單的測試腳本發展成一個完整的、專業的自動化測試框架。這個框架具有以下特點:
- 結構清晰:遵循頁面對象模型設計模式
- 可維護性強:元素定位與業務邏輯分離
- 可擴展性好:易于添加新的測試用例和頁面
- 穩定性高:智能等待機制減少flaky tests
- 報告完善:豐富的測試報告和日志
這種框架設計不僅適用于圖片上傳測試,也可以擴展到其他類型的Web UI自動化測試場景中,為軟件質量保障提供強有力的支持。
希望本文對您構建自己的自動化測試框架有所幫助!