news 2026/7/1 21:28:11

从零搭建Python+Selenium自动化测试框架:POM设计、Pytest集成与工程化实践

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
从零搭建Python+Selenium自动化测试框架:POM设计、Pytest集成与工程化实践

1. 项目概述:为什么我们需要自己的自动化测试框架?

如果你是一名测试工程师,或者正在向这个方向转型,你肯定不止一次听过“自动化测试”这个词。它听起来很美好,能解放重复劳动、提升回归效率、保证交付质量。但现实往往是,团队要么在用一些零散的脚本,维护成本高得吓人;要么在用一个庞大而笨重的商业工具,学习曲线陡峭,定制化需求难以满足。这时候,一个轻量、可控、贴合自身业务需求的自动化测试框架,就成了刚需。

这个项目,就是带你从零开始,用 Python 和 Selenium 搭建一个属于你自己的、可扩展的自动化测试框架。它不是教你写几个孤立的测试脚本,而是构建一个完整的工程体系。你会学到如何组织测试用例、如何管理测试数据、如何生成直观的报告、如何处理各种环境依赖,以及如何让这个框架在团队中真正用起来。Python 以其简洁和丰富的生态成为首选,Selenium 则是 Web 自动化测试领域事实上的标准。通过这个实战,你得到的不仅是一套代码,更是一套解决测试自动化工程问题的思维和方法。

2. 框架整体设计与核心思路拆解

2.1 框架设计目标与选型考量

在动手写第一行代码之前,我们必须想清楚这个框架要达成什么目标。一个合格的自动化测试框架,至少应该满足以下几点:可维护性(代码结构清晰,易于修改和扩展)、可读性(测试用例像文档一样清晰)、稳定性(能妥善处理网络波动、元素加载等不稳定因素)、易用性(新人能快速上手编写用例)以及可集成性(能方便地接入 CI/CD 流水线)。

基于这些目标,我们选择Python + Selenium + Pytest作为技术栈的核心。

  • Python:语法简洁,学习成本低,拥有极其丰富的第三方库(如 requests, pandas, openpyxl),能轻松处理测试数据、报告生成等周边任务。
  • Selenium:支持所有主流浏览器,社区活跃,是 Web UI 自动化的基石。虽然近年来 Playwright 和 Cypress 等新工具势头很猛,但 Selenium 的成熟度、生态广度以及“一次编写,多浏览器运行”的能力,使其仍然是构建企业级框架的稳妥选择。
  • Pytest:相比于 Python 自带的 unittest,Pytest 更灵活、功能更强大。它支持丰富的插件(如生成 HTML 报告的 pytest-html、控制用例执行顺序的 pytest-ordering)、简单的 fixture 机制来管理测试前置和后置条件,以及参数化测试,能极大提升测试用例的编写效率和可读性。

为什么不直接用录屏工具或者 Selenium IDE?因为它们生成的脚本通常是线性的、硬编码的,缺乏结构化和复用性,难以应对复杂的测试场景和持续的维护。我们的框架,是要面向工程化的。

2.2 框架核心目录结构规划

一个清晰的目录结构是框架可维护性的基石。在项目根目录下,我通常会这样组织:

automation_framework/ ├── configs/ # 配置文件目录 │ ├── config.ini # 主配置文件(数据库、URL、日志级别等) │ └── browser_config.json # 浏览器特定配置(窗口大小、无头模式、下载路径等) ├── data/ # 测试数据目录 │ ├── test_data.xlsx # Excel 存储的测试数据 │ └── api_data.json # JSON 存储的 API 测试数据 ├── logs/ # 日志文件目录(.gitignore) ├── reports/ # 测试报告目录(.gitignore) │ └── assets/ # 报告依赖的图片等资源 ├── page_objects/ # 页面对象模型(Page Object)目录 │ ├── __init__.py │ ├── base_page.py # 所有页面对象的基类 │ ├── login_page.py # 登录页面 │ └── home_page.py # 主页 ├── test_cases/ # 测试用例目录 │ ├── __init__.py │ ├── conftest.py # Pytest 的共享 fixture 配置 │ ├── test_login.py # 登录相关测试用例 │ └── test_search.py # 搜索相关测试用例 ├── utils/ # 工具函数目录 │ ├── __init__.py │ ├── logger.py # 自定义日志模块 │ ├── webdriver_helper.py # WebDriver 封装与工具函数 │ └── data_reader.py # 数据读取工具 ├── conftest.py # 项目根目录的全局 fixture ├── pytest.ini # Pytest 全局配置文件 └── requirements.txt # Python 依赖包列表

这个结构将配置、数据、页面对象、用例、工具和输出物清晰分离。page_objects目录是实现Page Object Model (POM)设计模式的关键,它将页面的元素定位和操作封装成类,让测试用例只关注业务逻辑,极大提升了代码的可维护性和复用性。

3. 核心模块详解与实操要点

3.1 环境搭建与依赖管理

第一步是准备好战场。你需要安装 Python(推荐 3.8 及以上版本),然后使用 pip 安装核心依赖。强烈建议使用虚拟环境(如venv)来隔离项目依赖。

  1. 创建并激活虚拟环境

    # 在项目根目录下 python -m venv venv # Windows venv\Scripts\activate # macOS/Linux source venv/bin/activate
  2. 安装核心包:将以下内容保存到requirements.txt,然后执行pip install -r requirements.txt

    selenium>=4.0.0 pytest>=7.0.0 pytest-html>=3.0.0 pytest-rerunfailures>=10.0 # 用于失败重试 webdriver-manager>=3.0.0 # 自动管理浏览器驱动,强烈推荐! openpyxl>=3.0.0 # 读写 Excel 测试数据 PyYAML>=6.0 # 读写 YAML 配置文件 allure-pytest>=2.0.0 # 可选,生成更美观的 Allure 报告

    webdriver-manager是一个神器,它能自动下载和匹配对应版本的浏览器驱动(ChromeDriver, GeckoDriver等),彻底告别手动下载和配置驱动路径的烦恼。

  3. IDE 配置:使用 VSCode 或 PyCharm。在 VSCode 中,确保选择正确的 Python 解释器(你的虚拟环境),并安装 Python 和 Pytest 相关插件。

注意:国内网络环境安装包可能较慢,可以配置 pip 镜像源,例如使用清华源:pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple

3.2 封装 WebDriver:打造稳健的浏览器操作核心

直接使用原始的webdriver.Chrome()在复杂项目中会很快变得难以维护。我们需要一个封装类来统一管理浏览器的生命周期、提供增强的查找元素方法、并集成自动等待和日志记录。

utils/webdriver_helper.py中:

import logging from selenium import webdriver from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC from selenium.common.exceptions import TimeoutException, NoSuchElementException from webdriver_manager.chrome import ChromeDriverManager from webdriver_manager.firefox import GeckoDriverManager class WebDriverHelper: def __init__(self, browser='chrome', headless=False, implicit_wait=10): """ 初始化 WebDriver。 :param browser: 浏览器类型,'chrome' 或 'firefox' :param headless: 是否无头模式(不显示浏览器界面) :param implicit_wait: 隐式等待时间(秒) """ self.logger = logging.getLogger(__name__) self.driver = None self.browser = browser self.headless = headless self._init_driver() self.driver.implicitly_wait(implicit_wait) self.wait = WebDriverWait(self.driver, 10) # 显式等待对象 def _init_driver(self): """根据配置初始化 WebDriver 实例。""" try: if self.browser.lower() == 'chrome': options = webdriver.ChromeOptions() if self.headless: options.add_argument('--headless') options.add_argument('--disable-gpu') options.add_argument('--no-sandbox') options.add_argument('--disable-dev-shm-usage') # 禁止 Chrome 受自动化控制提示,避免被某些网站检测 options.add_experimental_option("excludeSwitches", ["enable-automation"]) options.add_experimental_option('useAutomationExtension', False) # 使用 webdriver-manager 自动管理驱动 service = webdriver.ChromeService(ChromeDriverManager().install()) self.driver = webdriver.Chrome(service=service, options=options) elif self.browser.lower() == 'firefox': options = webdriver.FirefoxOptions() if self.headless: options.add_argument('--headless') service = webdriver.FirefoxService(GeckoDriverManager().install()) self.driver = webdriver.Firefox(service=service, options=options) else: raise ValueError(f"不支持的浏览器类型: {self.browser}") self.logger.info(f"{self.browser} 浏览器启动成功,无头模式: {self.headless}") except Exception as e: self.logger.error(f"启动 {self.browser} 浏览器失败: {e}") raise def find_element(self, locator_type, locator_value, timeout=10): """ 增强的元素查找方法,集成显式等待。 :param locator_type: 定位器类型,如 'id', 'xpath', 'css_selector', 'name' :param locator_value: 定位器值 :param timeout: 超时时间(秒) :return: WebElement 对象 """ locator = (locator_type, locator_value) try: element = WebDriverWait(self.driver, timeout).until( EC.presence_of_element_located(locator) ) self.logger.debug(f"找到元素: {locator}") return element except TimeoutException: self.logger.error(f"查找元素超时: {locator}") # 这里可以附加截图,方便调试 self.take_screenshot(f"element_not_found_{locator_type}_{locator_value}") raise NoSuchElementException(f"元素未找到: {locator}") def take_screenshot(self, name): """截图并保存到指定路径。""" screenshot_path = f"./logs/screenshot_{name}_{datetime.now().strftime('%Y%m%d_%H%M%S')}.png" self.driver.save_screenshot(screenshot_path) self.logger.info(f"截图已保存: {screenshot_path}") return screenshot_path def quit(self): """关闭浏览器。""" if self.driver: self.driver.quit() self.logger.info("浏览器已关闭")

这个封装类提供了几个关键优势:1)自动驱动管理,无需手动下载;2)统一的元素查找,内置显式等待,更稳定;3)灵活的配置,支持多浏览器和无头模式;4)集成日志和截图,便于调试。

3.3 实现 Page Object Model (POM)

POM 是 UI 自动化测试的黄金设计模式。其核心思想是将每个页面封装成一个类,页面上的元素定位器作为类的属性,页面操作(如输入、点击)作为类的方法。测试用例则通过调用这些页面对象的方法来完成业务流。

首先,在page_objects/base_page.py中定义一个所有页面的基类:

from utils.webdriver_helper import WebDriverHelper import logging class BasePage: def __init__(self, driver: WebDriverHelper): self.driver = driver self.logger = logging.getLogger(__name__) def open(self, url): """打开指定URL。""" self.driver.get(url) self.logger.info(f"打开页面: {url}") # 可以在这里定义一些公共方法,比如等待页面加载完成的通用逻辑

然后,实现具体的页面,例如page_objects/login_page.py

from page_objects.base_page import BasePage class LoginPage(BasePage): # 元素定位器(推荐使用元组存储,便于维护) USERNAME_INPUT = ('id', 'username') PASSWORD_INPUT = ('id', 'password') LOGIN_BUTTON = ('xpath', '//button[@type="submit"]') ERROR_MSG = ('css_selector', '.alert-error') def enter_username(self, username): """输入用户名。""" elem = self.driver.find_element(*self.USERNAME_INPUT) # 解包元组 elem.clear() elem.send_keys(username) self.logger.debug(f"输入用户名: {username}") def enter_password(self, password): """输入密码。""" elem = self.driver.find_element(*self.PASSWORD_INPUT) elem.clear() elem.send_keys(password) self.logger.debug("输入密码") def click_login(self): """点击登录按钮。""" elem = self.driver.find_element(*self.LOGIN_BUTTON) elem.click() self.logger.debug("点击登录按钮") def get_error_message(self): """获取错误提示信息。""" try: elem = self.driver.find_element(*self.ERROR_MSG, timeout=3) # 短时间等待错误信息 return elem.text except: return None def login(self, username, password): """登录业务流程。""" self.enter_username(username) self.enter_password(password) self.click_login()

通过 POM,当登录页面的输入框 ID 从username改为user时,你只需要在一个地方(LoginPage类)修改USERNAME_INPUT这个定位器,所有用到这个输入框的测试用例都无需改动,维护成本大大降低。

4. 测试用例编写与数据驱动

4.1 使用 Pytest 编写结构化测试用例

有了页面对象,编写测试用例就变得非常清晰。在test_cases/test_login.py中:

import pytest from page_objects.login_page import LoginPage from utils.data_reader import read_test_data_from_excel class TestLogin: """登录功能测试类。""" @pytest.fixture(autouse=True) def setup(self, driver): """每个测试方法执行前的准备工作。""" self.driver = driver self.login_page = LoginPage(self.driver) self.login_page.open("https://your-test-site.com/login") yield # 每个测试方法执行后的清理工作(如果需要) # self.driver.delete_all_cookies() @pytest.mark.parametrize("username, password, expected", [ ("admin", "correct_password", "success"), ("admin", "wrong_password", "invalid_credentials"), ("", "some_password", "username_required"), ("admin", "", "password_required"), ]) def test_login_with_different_inputs(self, username, password, expected): """ 数据驱动测试:使用不同组合测试登录功能。 """ self.login_page.login(username, password) if expected == "success": # 验证登录成功,例如跳转到首页或出现欢迎信息 assert "dashboard" in self.driver.current_url else: # 验证出现相应的错误提示 error_msg = self.login_page.get_error_message() assert error_msg is not None if expected == "invalid_credentials": assert "用户名或密码错误" in error_msg # ... 其他错误断言 def test_login_success(self): """正向用例:使用正确的凭据登录。""" # 测试数据可以来自外部文件 test_data = read_test_data_from_excel("data/test_data.xlsx", "login_success") self.login_page.login(test_data['username'], test_data['password']) # 断言登录成功后的状态 assert self.driver.current_url == "https://your-test-site.com/dashboard" # 或者断言页面包含某个成功元素 welcome_elem = self.driver.find_element('css_selector', '.welcome-msg') assert test_data['username'] in welcome_elem.text

这里展示了 Pytest 的几个强大功能:@pytest.fixture用于管理测试资源(如 driver),autouse=True让它在每个测试方法中自动执行;@pytest.mark.parametrize实现了数据驱动测试,用一个测试函数覆盖多组数据;清晰的断言使得测试意图一目了然。

4.2 测试数据管理

硬编码的测试数据是维护的噩梦。我们将数据剥离到外部文件。utils/data_reader.py可以这样写:

import openpyxl import json import yaml from pathlib import Path def read_test_data_from_excel(file_path, sheet_name, key=None): """ 从 Excel 文件读取测试数据。 :param file_path: Excel 文件路径 :param sheet_name: 工作表名 :param key: 可选,如果提供,返回该键对应的行数据(字典) :return: 字典或字典列表 """ workbook = openpyxl.load_workbook(file_path, data_only=True) sheet = workbook[sheet_name] # 读取表头 headers = [cell.value for cell in next(sheet.iter_rows(min_row=1, max_row=1))] data = [] for row in sheet.iter_rows(min_row=2, values_only=True): # 从第二行开始 row_data = dict(zip(headers, row)) data.append(row_data) workbook.close() if key and data: # 假设第一列是测试用例的标识键 for item in data: if item[headers[0]] == key: return item raise ValueError(f"未找到键为 '{key}' 的测试数据") return data def read_config_from_yaml(file_path): """从 YAML 文件读取配置。""" with open(file_path, 'r', encoding='utf-8') as f: return yaml.safe_load(f) # 示例:读取 JSON 格式的 API 测试数据 def read_api_data(file_path): with open(file_path, 'r', encoding='utf-8') as f: return json.load(f)

在 Excel 中,你可以这样组织login工作表:

TestCaseIDusernamepasswordexpected_resultexpected_message
TC_LOGIN_001admincorrect_pwdsuccess
TC_LOGIN_002adminwrong_pwdfail用户名或密码错误
TC_LOGIN_003some_pwdfail用户名不能为空

这样,测试逻辑和数据完全分离。新增测试用例时,只需在 Excel 中添加一行,无需修改代码。

5. 测试执行、报告与高级配置

5.1 配置 Pytest 并生成测试报告

在项目根目录创建pytest.ini文件,这是 Pytest 的主配置文件:

[pytest] # 指定测试文件的位置和模式 testpaths = test_cases python_files = test_*.py python_classes = Test* python_functions = test_* # 添加命令行默认选项 addopts = -v # 详细输出 --html=reports/report.html # 生成 HTML 报告 --self-contained-html # 将 CSS 等嵌入 HTML,使报告单文件化 --reruns 1 # 失败用例重试1次 --reruns-delay 2 # 重试间隔2秒 --capture=sys # 捕获输出 -l # 显示失败测试的局部变量 # 定义标记,用于分类运行测试 markers = smoke: 冒烟测试用例 regression: 回归测试用例 slow: 运行较慢的测试用例

现在,在命令行中运行pytest,它会自动按照配置执行test_cases目录下所有test_*.py文件中的测试,并生成一份详细的 HTML 报告在reports/report.html。报告里会包含通过/失败数量、执行时间、失败用例的错误信息和截图(如果集成了的话),非常适合在团队中分享结果。

5.2 使用 Fixture 管理测试生命周期

conftest.py是 Pytest 的本地插件文件,在这里定义的 fixture 可以被同一目录及子目录下的所有测试文件使用。我们在项目根目录和test_cases目录下各放一个,实现不同层级的共享。

项目根目录的conftest.py负责最核心的资源管理:

import pytest import logging from utils.webdriver_helper import WebDriverHelper from utils.logger import setup_logger # 设置日志 setup_logger() @pytest.fixture(scope="session") def config(): """读取全局配置(示例)。""" # 这里可以从 config.ini 或 YAML 文件读取 return { "base_url": "https://your-test-site.com", "browser": "chrome", "headless": False, "timeout": 10 } @pytest.fixture(scope="function") # 默认是 function 级别,每个测试函数一个 driver def driver(config): """ 最重要的 fixture:为每个测试用例提供 WebDriver 实例。 scope="function" 确保每个测试用例都有干净的浏览器会话。 """ driver_helper = None try: driver_helper = WebDriverHelper( browser=config.get("browser", "chrome"), headless=config.get("headless", False) ) driver_helper.driver.maximize_window() yield driver_helper.driver # 将 driver 对象传递给测试用例 finally: # 无论测试成功还是失败,最后都会关闭浏览器 if driver_helper: driver_helper.quit() @pytest.fixture(scope="session", autouse=True) def global_setup_teardown(): """会话级别的 setup 和 teardown,整个测试会话只执行一次。""" logging.info("=== 开始自动化测试会话 ===") yield logging.info("=== 自动化测试会话结束 ===")

test_cases/conftest.py则可以定义一些测试用例目录特有的 fixture,比如登录状态的 fixture:

import pytest from page_objects.login_page import LoginPage @pytest.fixture def logged_in_driver(driver): """提供一个已登录状态的 driver。""" login_page = LoginPage(driver) login_page.open("https://your-test-site.com/login") login_page.login("standard_user", "secret_sauce") # 使用一个测试账号 yield driver # 登出清理(如果需要) # driver.get("https://your-test-site.com/logout")

在测试用例中,你只需要在参数中声明需要的 fixture,Pytest 会自动注入:

def test_something_with_login(logged_in_driver): # 使用已登录的 driver home_page = HomePage(logged_in_driver) # ... 测试逻辑 def test_something_else(driver): # 使用全新的 driver # ... 测试逻辑

这种设计让测试用例的编写非常简洁,且资源管理井井有条。

5.3 日志记录与失败截图

良好的日志是调试和追溯问题的生命线。utils/logger.py

import logging import sys from pathlib import Path from datetime import datetime def setup_logger(name=__name__, log_level=logging.INFO): """配置并返回一个日志记录器。""" # 创建 logs 目录 log_dir = Path("logs") log_dir.mkdir(exist_ok=True) # 生成带时间的日志文件名 log_file = log_dir / f"automation_{datetime.now().strftime('%Y%m%d')}.log" # 获取 logger logger = logging.getLogger(name) logger.setLevel(log_level) # 避免重复添加 handler if logger.handlers: return logger # 格式化器 formatter = logging.Formatter( '%(asctime)s - %(name)s - %(levelname)s - %(message)s', datefmt='%Y-%m-%d %H:%M:%S' ) # 文件处理器 file_handler = logging.FileHandler(log_file, encoding='utf-8') file_handler.setFormatter(formatter) logger.addHandler(file_handler) # 控制台处理器 console_handler = logging.StreamHandler(sys.stdout) console_handler.setFormatter(formatter) logger.addHandler(console_handler) return logger

在 WebDriver 封装和页面对象中,我们都使用这个 logger 来记录关键操作和错误。结合 Pytest 的@pytest.hookimpl钩子函数,我们可以在测试失败时自动截图。在conftest.py中添加:

@pytest.hookimpl(hookwrapper=True) def pytest_runtest_makereport(item, call): """ Pytest 钩子:在每个测试步骤后生成报告,并在失败时截图。 """ outcome = yield report = outcome.get_result() if report.when == "call" and report.failed: # 获取测试用例中的 driver fixture driver_fixture = item.funcargs.get('driver') if driver_fixture: # 调用我们封装好的截图方法 screenshot_path = f"./logs/failure_{item.name}_{datetime.now().strftime('%H%M%S')}.png" driver_fixture.save_screenshot(screenshot_path) # 将截图路径附加到测试报告中 if hasattr(report, 'extra'): import pytest_html report.extra.append(pytest_html.extras.image(screenshot_path, '失败截图')) report.extra.append(pytest_html.extras.html(f'<div>截图: <a href="{screenshot_path}">{screenshot_path}</a></div>'))

这样,每次测试失败,都会在logs目录下生成一张截图,并链接到 HTML 报告中,让你能直观地看到失败时的页面状态。

6. 常见问题排查与实战技巧

6.1 元素定位失败问题精讲

这是 Selenium 自动化中最常见的问题,没有之一。其根源通常在于“时机不对”或“定位器不对”。

  1. 动态元素与等待策略

    • 问题:代码执行速度远快于页面加载或 JavaScript 渲染速度,导致在元素出现之前就去查找它。
    • 解决永远不要依赖固定的time.sleep()。使用显式等待(Explicit Wait)。我们封装在WebDriverHelper.find_element中的WebDriverWait就是干这个的。它会在指定时间内不断尝试查找元素,直到找到或超时。
    • 进阶技巧:等待条件不仅仅是presence_of_element_located(元素存在于 DOM),还有visibility_of_element_located(元素可见)、element_to_be_clickable(元素可点击)等。根据你的实际操作选择合适的条件。
      # 等待元素可见并可点击 from selenium.webdriver.support import expected_conditions as EC element = WebDriverWait(driver, 10).until( EC.element_to_be_clickable((By.ID, "dynamic-button")) ) element.click()
  2. 定位器不稳定

    • 问题:使用了绝对 XPath(如/html/body/div[3]/div[2]/form/input[1]),页面结构微调就会导致定位失败。或者使用了容易变化的 ID/Class(如带随机后缀id="submit-btn-12345")。
    • 解决
      • 优先级id>name>css selector>xpath。ID 通常是唯一且最稳定的。
      • 编写稳健的 CSS Selector 和 XPath
        • CSS: 避免使用依赖位置的结构。如input.form-control[name='username']div.container > form > div:nth-child(2) > input好得多。
        • XPath: 使用属性、文本内容或相对路径。如//button[contains(@class, 'btn-primary') and text()='提交']//input[@placeholder='请输入用户名']
      • 与开发约定:为关键测试元素添加稳定的>iframe = driver.find_element('tag name', 'iframe') driver.switch_to.frame(iframe) # 现在可以定位 iframe 内的元素了 inner_element = driver.find_element('id', 'inner-elem') # 操作完成后切回主文档 driver.switch_to.default_content()
      • Shadow DOM:需要使用execute_script执行 JavaScript 来穿透 Shadow Root。
        # 假设有一个 shadow host 元素 shadow_host = driver.find_element('css selector', 'custom-element') # 获取其 shadow root shadow_root = driver.execute_script('return arguments[0].shadowRoot', shadow_host) # 现在可以在 shadow root 下查找元素 shadow_element = shadow_root.find_element('css selector', 'button')

6.2 测试稳定性提升技巧

  1. 失败重试机制:网络抖动、资源加载慢可能导致偶发性失败。使用pytest-rerunfailures插件(已在pytest.ini中配置--reruns 1)可以让失败的用例自动重试一次,提高稳定性,但需注意区分“偶发失败”和“真实缺陷”。

  2. 页面加载状态判断:对于单页应用(SPA),简单的driver.get()后页面可能并未真正就绪。可以等待某个关键元素出现,或者等待 JavaScript 的某个变量/状态。

    # 等待页面标题包含特定文本 WebDriverWait(driver, 10).until(EC.title_contains("Dashboard")) # 等待 jQuery(如果使用)活动完成 WebDriverWait(driver, 10).until(lambda d: d.execute_script('return jQuery.active == 0')) # 等待特定 JS 变量就绪 WebDriverWait(driver, 10).until(lambda d: d.execute_script('return window.appLoaded === true'))
  3. 处理弹窗和浏览器通知:在启动浏览器时通过Options禁用弹窗或设置默认行为。

    options = webdriver.ChromeOptions() prefs = { "profile.default_content_setting_values.notifications": 2, # 禁用通知 "download.default_directory": "/path/to/downloads", # 设置下载路径 } options.add_experimental_option("prefs", prefs)

6.3 框架集成与持续运行

  1. 命令行参数化:通过pytest.addoption钩子,可以让框架支持命令行参数,动态指定浏览器、环境等。

    # 在 conftest.py 中 def pytest_addoption(parser): parser.addoption("--browser", action="store", default="chrome", help="浏览器类型: chrome 或 firefox") parser.addoption("--env", action="store", default="staging", help="测试环境: staging 或 prod") @pytest.fixture(scope="session") def config(request): browser = request.config.getoption("--browser") env = request.config.getoption("--env") # 根据 env 加载不同的配置文件 env_config = load_config_for_env(env) return {"browser": browser, **env_config}

    运行命令:pytest --browser=firefox --env=prod

  2. 集成到 CI/CD (Jenkins/GitLab CI):在 CI 服务器上,通常以无头模式运行测试。确保你的框架支持--headless参数,并且所有依赖(包括浏览器)都能在无图形界面的服务器上正确安装。一个简单的 GitLab CI.gitlab-ci.yml配置示例:

    stages: - test automation-test: stage: test image: python:3.10-slim before_script: - apt-get update && apt-get install -y wget unzip chromium chromium-driver - pip install -r requirements.txt script: - pytest --headless --html=report.html artifacts: when: always paths: - reports/ - logs/ reports: junit: reports/junit.xml only: - main - merge_requests
  3. 测试报告与通知:除了 HTML 报告,还可以集成 Allure 生成更美观的交互式报告,或者将测试结果通过 Webhook 发送到团队聊天工具(如钉钉、飞书、Slack)。

搭建自动化测试框架是一个迭代的过程,不要试图一开始就设计得完美无缺。从核心的页面对象和基础用例开始,逐步添加日志、报告、数据驱动、CI 集成等模块。最重要的是,让框架尽快在真实项目中小范围跑起来,在实践中发现问题并持续改进。这个由你亲手搭建、完全可控的框架,将成为你提升测试效率和质量最得力的武器。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/1 21:24:43

基于changedetection.io的系统化网站变更监控解决方案

基于changedetection.io的系统化网站变更监控解决方案 【免费下载链接】changedetection.io Best and simplest tool for website change detection, web page monitoring, and website change alerts. Perfect for tracking content changes, price drops, restock alerts, an…

作者头像 李华
网站建设 2026/7/1 21:24:21

3分钟解锁QQ音乐格式限制:QMCFLAC2MP3让你的音乐真正自由

3分钟解锁QQ音乐格式限制&#xff1a;QMCFLAC2MP3让你的音乐真正自由 【免费下载链接】qmcflac2mp3 直接将qmcflac文件转换成mp3文件&#xff0c;突破QQ音乐的格式限制 项目地址: https://gitcode.com/gh_mirrors/qm/qmcflac2mp3 还在为QQ音乐下载的歌曲只能在特定播放器…

作者头像 李华
网站建设 2026/7/1 21:23:56

ICM-42688-P与PIC18F55K42在工业运动感知中的技术解析

1. ICM-42688-P与PIC18F55K42的黄金组合解析在工业级运动传感与嵌入式控制领域&#xff0c;TDK InvenSense的ICM-42688-P六轴MEMS惯性测量单元(IMU)与Microchip的PIC18F55K42微控制器形成的技术组合&#xff0c;正在重塑运动感知系统的设计范式。这对组合之所以能成为工业自动化…

作者头像 李华
网站建设 2026/7/1 21:23:12

基于Qwen3.5-9B与OpenClaw的AI驱动UI自动化测试实践

1. 项目概述&#xff1a;当大模型遇上UI自动化测试 最近在折腾一个挺有意思的项目&#xff0c;叫OpenClaw。简单来说&#xff0c;它试图解决一个自动化测试领域的老大难问题&#xff1a;写UI测试用例太费劲了。传统的UI自动化测试&#xff0c;无论是用Selenium、Playwright还是…

作者头像 李华
网站建设 2026/7/1 21:21:05

Python测试实战:从零构建可维护的pytest框架与工程化实践

1. 项目概述&#xff1a;为什么我们需要一场“实战演练”&#xff1f;如果你在Python测试领域待过一段时间&#xff0c;大概率已经听说过甚至用过pytest。它几乎成了现代Python自动化测试的代名词&#xff0c;网上教程铺天盖地&#xff0c;从“5分钟入门”到“高级Fixture用法”…

作者头像 李华
网站建设 2026/7/1 21:20:02

从代码示例到工程体系:构建稳定可维护的UI自动化测试框架实战

1. 项目概述&#xff1a;从“玩具”到“利器”的UI自动化 如果你问一个刚入行的测试工程师&#xff0c;UI自动化是什么&#xff0c;他可能会给你看一段用Selenium写的脚本&#xff0c;能打开浏览器&#xff0c;输入几个字&#xff0c;点个按钮。但如果你问一个在项目中真正用UI…

作者头像 李华