从零搭建Selenium自动化测试框架:POM模式与pytest实战指南
1. 项目概述为什么我们需要一个稳固的Selenium框架在软件工程的世界里自动化测试早已不是“锦上添花”而是保障产品质量、提升交付效率的“生命线”。尤其是Web应用其界面复杂、交互频繁靠人工点点点不仅效率低下更难以保证回归测试的全面性。Selenium作为Web自动化测试领域的“老炮儿”以其对主流浏览器的广泛支持和灵活的编程接口成为了无数测试工程师和开发者的首选工具。但很多新手甚至一些有经验的团队常会陷入一个误区认为自动化测试就是写几个脚本能跑起来就行。结果往往是脚本脆弱不堪浏览器一升级就挂用例之间相互污染测试报告不知所云维护成本高到令人崩溃。这背后的核心问题就是缺乏一个设计良好的测试框架。一个成熟的Selenium框架绝不仅仅是封装几个find_element和click方法。它是一个系统工程需要解决环境管理、用例组织、数据驱动、异常处理、报告生成和持续集成等一系列问题。它决定了自动化测试的可维护性、可读性、稳定性和可扩展性。今天我就结合自己多年踩坑填坑的经验从零开始手把手带你搭建一个面向实战、结构清晰、易于维护的Selenium自动化测试框架。无论你是正在学习软件工程的学生还是希望提升团队自动化水平的工程师这篇攻略都将为你提供一条清晰的路径和大量可直接“抄作业”的实操细节。2. 框架核心设计与架构选型在动手写第一行代码之前我们必须想清楚框架要解决什么问题以及采用什么样的架构。盲目开始后期必然要推倒重来。2.1 核心需求与设计目标我们的框架需要满足以下几个核心目标环境隔离与可移植性测试脚本能在不同开发者的机器上、以及CI/CD流水线中稳定运行不受本地浏览器版本、驱动路径的干扰。用例与代码分离测试逻辑操作步骤与测试数据输入、预期结果分离便于数据驱动测试。健壮性与容错性能智能处理页面加载延迟、元素查找失败、弹窗等常见异常避免因偶发问题导致整个测试套件失败。清晰的报告与日志测试结束后能生成一目了然的测试报告记录成功、失败、错误并附上截图和日志方便快速定位问题。易于集成与执行能够方便地通过命令行触发、按模块或标签筛选用例并能无缝集成到Jenkins、GitLab CI等持续集成工具中。基于这些目标一个典型的分层架构是最佳选择。它将不同的职责划分到不同的层遵循单一职责原则。2.2 技术栈与工具选型这里我推荐一个经过大量项目验证的、以Python语言为主的经典技术栈组合编程语言Python 3.8。语法简洁生态丰富是自动化测试领域的事实标准。相比Java或C#Python能让你更专注于测试逻辑本身。测试框架pytest。它比Python自带的unittest更强大、更灵活。支持丰富的插件如并行执行、html报告、灵活的fixture机制用于测试前置和后置条件以及简洁的断言语法。浏览器驱动管理webdriver-manager。这是一个神器它能自动下载、匹配并管理Chrome、Firefox等浏览器的驱动程序彻底解决“驱动版本不匹配”这个令人头疼的问题。页面对象模式库Page Object Model (POM)。这不是一个具体的库而是一种设计模式。我们将每个页面或页面组件封装成一个类页面的元素定位器和操作行为作为类的方法。这能极大提高代码的可维护性和复用性。报告生成pytest-html或Allure。pytest-html简单易用能快速生成一个包含测试结果的HTML报告。Allure功能强大能生成非常美观、交互性强的测试报告支持步骤描述、附件截图、日志、历史趋势分析等是追求专业报告效果的首选。配置管理configparser处理.ini文件或PyYAML处理.yaml文件。用于统一管理测试环境URL、数据库连接、用户凭证等配置信息。数据驱动pytest自带的pytest.mark.parametrize装饰器或者使用openpyxl/pandas读取Excel/CSV文件作为数据源。注意工具选型不是一成不变的。例如如果你的团队主要用Java那么TestNG Selenium Maven是更自然的选择。但核心的分层架构思想和设计模式如POM是相通的。本文以Python技术栈为例进行讲解其设计理念可以平移到其他语言。3. 项目结构搭建与核心模块解析一个清晰的项目结构是框架可维护性的基石。下面是我推荐的一个标准目录结构你可以根据项目规模进行调整。your_automation_framework/ ├── configs/ # 配置文件目录 │ ├── config.ini # 主配置文件环境、URL等 │ └── test_data.yaml # 测试数据文件 ├── drivers/ # 可选存放本地浏览器驱动优先用webdriver-manager ├── logs/ # 日志文件输出目录自动生成 ├── reports/ # 测试报告输出目录自动生成 │ └── allure-results/ # Allure原始结果数据 ├── screenshots/ # 失败用例截图目录自动生成 ├── src/ # 框架核心源代码 │ ├── core/ # 核心基础模块 │ │ ├── __init__.py │ │ ├── base_page.py # 所有页面对象的基类 │ │ ├── base_test.py # 所有测试用例的基类 │ │ ├── driver_factory.py # 浏览器驱动创建工厂 │ │ ├── logger.py # 自定义日志记录器 │ │ └── wait_conditions.py # 自定义等待条件 │ ├── pages/ # 页面对象层 │ │ ├── __init__.py │ │ ├── login_page.py # 登录页面 │ │ ├── home_page.py # 主页 │ │ └── ... # 其他页面 │ └── utils/ # 工具函数层 │ ├── __init__.py │ ├── file_reader.py # 文件读取工具 │ └── helper.py # 通用辅助函数 ├── tests/ # 测试用例层 │ ├── __init__.py │ ├── conftest.py # pytest共享fixture配置 │ ├── test_login.py # 登录模块测试用例 │ └── test_search.py # 搜索模块测试用例 ├── .gitignore # Git忽略文件 ├── requirements.txt # Python依赖包列表 ├── pytest.ini # pytest配置文件 └── README.md # 项目说明文档接下来我们深入几个最核心的模块看看它们具体如何实现。3.1 驱动管理工厂 (driver_factory.py)这个模块的核心职责是创建并返回一个配置好的WebDriver实例。使用工厂模式可以让我们灵活地支持多种浏览器并集中管理驱动配置。# src/core/driver_factory.py from selenium import webdriver from selenium.webdriver.chrome.service import Service as ChromeService from selenium.webdriver.firefox.service import Service as FirefoxService from webdriver_manager.chrome import ChromeDriverManager from webdriver_manager.firefox import GeckoDriverManager from src.core.logger import get_logger logger get_logger(__name__) class DriverFactory: staticmethod def get_driver(browser_namechrome, headlessFalse, optionsNone): 获取WebDriver实例。 :param browser_name: 浏览器名称支持 chrome, firefox, edge :param headless: 是否启用无头模式不显示浏览器界面 :param options: 自定义的浏览器选项对象 :return: WebDriver实例 driver None try: if browser_name.lower() chrome: chrome_options options or webdriver.ChromeOptions() if headless: chrome_options.add_argument(--headlessnew) # 新版Chrome无头模式 chrome_options.add_argument(--no-sandbox) chrome_options.add_argument(--disable-dev-shm-usage) chrome_options.add_argument(--disable-gpu) chrome_options.add_argument(--window-size1920,1080) # 使用webdriver-manager自动管理驱动 service ChromeService(ChromeDriverManager().install()) driver webdriver.Chrome(serviceservice, optionschrome_options) logger.info(Chrome driver initialized successfully.) elif browser_name.lower() firefox: firefox_options options or webdriver.FirefoxOptions() if headless: firefox_options.add_argument(--headless) # 使用webdriver-manager自动管理驱动 service FirefoxService(GeckoDriverManager().install()) driver webdriver.Firefox(serviceservice, optionsfirefox_options) logger.info(Firefox driver initialized successfully.) # ... 可以继续扩展Edge等浏览器 else: raise ValueError(fUnsupported browser: {browser_name}) # 全局隐式等待非必须推荐使用显式等待 driver.implicitly_wait(10) # 最大化窗口 driver.maximize_window() return driver except Exception as e: logger.error(fFailed to initialize {browser_name} driver: {e}) raise关键点解析webdriver-manager这是代码稳定的关键。它自动处理驱动的下载和版本匹配你不再需要手动下载chromedriver.exe并配置PATH。无头模式headless参数对于在CI服务器如Linux上运行测试至关重要因为没有图形界面。Chrome选项--no-sandbox和--disable-dev-shm-usage是解决在Docker或某些Linux环境中运行Chrome常见问题的参数。异常处理与日志对驱动创建过程进行try-except包装并通过日志记录成功或失败信息便于排查环境问题。3.2 页面对象基类 (base_page.py)这是实现POM模式的核心。基类封装了所有页面对象共用的方法如元素查找、点击、输入、等待等。# src/core/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, StaleElementReferenceException from src.core.logger import get_logger logger get_logger(__name__) class BasePage: def __init__(self, driver): self.driver driver self.wait WebDriverWait(driver, timeout10, poll_frequency0.5, ignored_exceptions[StaleElementReferenceException]) def find_element(self, locator): 查找单个元素使用显式等待确保元素存在且可见 logger.debug(fLooking for element with locator: {locator}) try: element self.wait.until(EC.visibility_of_element_located(locator)) return element except TimeoutException: logger.error(fElement not found within timeout: {locator}) # 这里可以附加截图我们稍后在BaseTest中统一处理 raise def click(self, locator): 点击元素 element self.find_element(locator) logger.info(fClicking on element: {locator}) element.click() def input_text(self, locator, text): 向输入框输入文本先清空原有内容 element self.find_element(locator) logger.info(fInputting text {text} into element: {locator}) element.clear() element.send_keys(text) def get_text(self, locator): 获取元素的文本内容 element self.find_element(locator) text element.text logger.debug(fGot text {text} from element: {locator}) return text def is_element_present(self, locator, timeout5): 检查元素是否存在不要求可见返回布尔值 try: WebDriverWait(self.driver, timeout).until(EC.presence_of_element_located(locator)) return True except TimeoutException: return False def switch_to_new_window(self, original_window): 切换到新打开的窗口 for window_handle in self.driver.window_handles: if window_handle ! original_window: self.driver.switch_to.window(window_handle) logger.info(Switched to new window.) break设计思路封装与复用将Selenium原生的、可能出错的find_element_by_*和click()等操作封装起来内部加入显式等待和日志使页面对象类更简洁、健壮。显式等待这是自动化测试稳定的“黄金法则”。WebDriverWait配合expected_conditions可以等待元素满足特定条件如可见、可点击后再操作避免了因网络或JS加载慢导致的NoSuchElementException。日志记录在每个关键操作前后记录日志当测试失败时通过查看日志能快速定位到是哪一步出了问题。3.3 测试用例基类与pytest配置 (base_test.py,conftest.py)base_test.py为所有测试用例提供公共的setup和teardown逻辑。而conftest.py是pytest特有的文件用于定义在整个测试会话或模块中可用的fixture。# src/core/base_test.py import pytest from src.core.driver_factory import DriverFactory from src.core.logger import get_logger import os from datetime import datetime logger get_logger(__name__) class BaseTest: pytest.fixture(scopeclass, autouseTrue) def setup_class(self, request): 测试类级别的初始化整个类只执行一次 logger.info(f Starting Test Class: {request.cls.__name__} ) # 从pytest命令行参数或配置中获取浏览器类型 browser request.config.getoption(--browser) or chrome headless request.config.getoption(--headless) or False self.driver DriverFactory.get_driver(browser_namebrowser, headlessheadless) self.driver.implicitly_wait(5) # 将driver实例绑定到测试类方便页面对象使用 request.cls.driver self.driver yield # 此处是测试用例执行的分界线 # 测试类结束后执行清理 logger.info(f Tearing down Test Class: {request.cls.__name__} ) if self.driver: self.driver.quit() pytest.fixture(autouseTrue) def setup_method(self): 每个测试方法执行前的初始化 # 可以在这里做一些每个用例前的准备工作比如访问首页 # self.driver.get(https://www.example.com) pass def take_screenshot(self, name): 截图并保存到指定目录 screenshot_dir screenshots os.makedirs(screenshot_dir, exist_okTrue) timestamp datetime.now().strftime(%Y%m%d_%H%M%S) file_path os.path.join(screenshot_dir, f{name}_{timestamp}.png) self.driver.save_screenshot(file_path) logger.info(fScreenshot saved to: {file_path}) return file_path# tests/conftest.py import pytest from src.core.logger import setup_logging def pytest_addoption(parser): 添加自定义命令行参数 parser.addoption( --browser, actionstore, defaultchrome, helpBrowser to run tests: chrome or firefox ) parser.addoption( --headless, actionstore_true, defaultFalse, helpRun tests in headless mode ) parser.addoption( --env, actionstore, defaulttest, helpEnvironment to run tests: test, staging, prod ) pytest.fixture(scopesession, autouseTrue) def configure_logging(): 会话开始时配置日志 setup_logging() yield pytest.hookimpl(tryfirstTrue, hookwrapperTrue) def pytest_runtest_makereport(item, call): Hook函数用于在测试用例执行后获取结果并自动为失败的用例截图。 outcome yield report outcome.get_result() if report.when call and report.failed: # 如果测试用例执行失败且处于call阶段 # 尝试从测试用例实例中获取driver并截图 test_instance item.instance if hasattr(test_instance, driver) and test_instance.driver: screenshot_path test_instance.take_screenshot(item.name) # 可以将截图路径附加到Allure报告中如果使用Allure if hasattr(report, extra): import allure allure.attach.file(screenshot_path, name失败截图, attachment_typeallure.attachment_type.PNG)conftest.py的妙用pytest_addoption让你可以通过命令行pytest --browserfirefox --headless来动态控制测试运行环境。pytest_runtest_makereport这是一个强大的pytest钩子。它能在每个测试用例执行后介入如果用例失败则自动调用我们写的take_screenshot方法截图并可以将截图附加到Allure报告中。这实现了失败用例的自动留痕是调试的利器。4. 编写页面对象与测试用例有了稳固的基础设施现在我们来编写实际的测试。假设我们要测试一个登录功能。4.1 创建登录页面对象首先在src/pages/login_page.py中定义登录页面的元素和操作。# src/pages/login_page.py from src.core.base_page import BasePage from selenium.webdriver.common.by import By class LoginPage(BasePage): # 定位器 (Locators) - 将元素定位方式集中管理 USERNAME_INPUT (By.ID, username) PASSWORD_INPUT (By.ID, password) LOGIN_BUTTON (By.XPATH, //button[typesubmit]) ERROR_MESSAGE (By.CLASS_NAME, alert-error) def __init__(self, driver): super().__init__(driver) # 可以在这里添加页面特定的初始化比如访问登录页URL # self.driver.get(https://example.com/login) def enter_username(self, username): self.input_text(self.USERNAME_INPUT, username) return self # 支持链式调用 def enter_password(self, password): self.input_text(self.PASSWORD_INPUT, password) return self def click_login(self): self.click(self.LOGIN_BUTTON) # 点击后页面可能会跳转返回下一个页面的对象如HomePage # 这里我们先返回自身实际项目中根据情况返回新页面对象 # from .home_page import HomePage # return HomePage(self.driver) def get_error_message(self): 获取登录失败时的错误提示信息 if self.is_element_present(self.ERROR_MESSAGE): return self.get_text(self.ERROR_MESSAGE) return None def login(self, username, password): 一个完整的登录操作流程 self.enter_username(username).enter_password(password).click_login()POM模式的优势可维护性如果登录页面的输入框ID从username变成了user你只需要修改USERNAME_INPUT这一个常量所有用到它的测试用例都无需改动。可读性测试用例读起来就像自然语言login_page.enter_username(“admin”).enter_password(“123456”).click_login()。复用性login方法封装了完整的登录流程任何需要登录的测试用例都可以直接调用。4.2 编写pytest测试用例接下来在tests/test_login.py中编写具体的测试用例。# tests/test_login.py import pytest from src.pages.login_page import LoginPage # 假设登录后跳转到主页 from src.pages.home_page import HomePage class TestLogin(BaseTest): # 继承自BaseTest自动获得driver和fixture 登录功能测试套件 pytest.mark.parametrize(username, password, expected, [ (correct_user, correct_pass, success), # 正向用例 (wrong_user, correct_pass, failure), # 反向用例-用户名错误 (correct_user, wrong_pass, failure), # 反向用例-密码错误 (, correct_pass, failure), # 边界用例-用户名为空 ]) def test_login_with_different_credentials(self, username, password, expected): 数据驱动测试使用多组数据测试登录功能。 login_page LoginPage(self.driver) # 访问登录页实际项目中URL应从配置读取 self.driver.get(https://your-test-site.com/login) login_page.login(username, password) if expected success: # 验证登录成功检查是否跳转到主页并且主页有用户信息 home_page HomePage(self.driver) assert home_page.is_user_logged_in(username), fLogin failed for user: {username} else: # 验证登录失败检查页面上出现了错误提示 error_msg login_page.get_error_message() assert error_msg is not None, Error message should be displayed for failed login. assert invalid in error_msg.lower() or incorrect in error_msg.lower(), fUnexpected error message: {error_msg} def test_login_with_remember_me(self): 测试‘记住我’功能 # 1. 登录时勾选‘记住我’ # 2. 关闭浏览器 # 3. 重新打开浏览器访问网站 # 4. 验证是否自动登录 # 实现略涉及Cookie操作 pass用例设计要点数据驱动使用pytest.mark.parametrize装饰器将测试数据与测试逻辑分离。一组数据就是一条测试用例pytest会自动生成并执行多条。这极大地减少了代码重复。断言清晰断言assert是测试的灵魂。断言条件要明确失败信息要清晰如f”Login failed for user: {username}”这样一看报告就知道哪里不对。用例独立性每个测试用例都应该是独立的不依赖于其他用例的执行状态。BaseTest中的setup_method虽然这里为空和pytest的默认运行机制保证了这一点。5. 运行测试与生成报告框架搭建好了用例也写完了最后一步就是如何优雅地执行并查看结果。5.1 配置pytest.ini在项目根目录创建pytest.ini文件统一配置pytest的运行行为。# pytest.ini [pytest] # 指定测试文件的位置和命名规则 testpaths tests python_files test_*.py python_classes Test* python_functions test_* # 添加命令行默认选项 addopts -v # 详细输出 --strict-markers # 严格检查marker --tbshort # 失败时输出简短的traceback --htmlreports/report.html # 生成html报告 --self-contained-html # 生成独立的html报告所有资源内联 # 自定义标记用于分类运行用例 markers smoke: 冒烟测试用例 regression: 回归测试用例 login: 登录模块测试 slow: 运行缓慢的测试5.2 使用Allure生成高级报告推荐虽然pytest-html简单但Allure报告在美观度和信息量上更胜一筹。安装Allure首先需要安装Java运行时环境JRE。然后从 Allure官网 下载命令行工具并配置到系统PATH。安装Python插件pip install allure-pytest修改pytest.ini中的addoptsaddopts -v --strict-markers --tbshort --alluredirreports/allure-results # 指定Allure结果数据目录运行测试并生成报告# 运行所有测试结果存入reports/allure-results pytest # 使用Allure命令行工具生成HTML报告 allure generate reports/allure-results -o reports/allure-report --clean # 打开报告 allure open reports/allure-reportAllure报告会展示测试套件的概览、通过率、趋势图并且可以查看每个用例的详细步骤、截图、日志甚至支持给测试步骤添加描述可读性极强。5.3 常用运行命令示例# 运行所有测试 pytest # 运行特定标记的测试如冒烟测试 pytest -m smoke # 运行指定文件中的测试 pytest tests/test_login.py # 运行包含特定关键词的测试 pytest -k login # 多进程并行运行测试加快速度需要安装pytest-xdist pytest -n 4 # 指定浏览器和无头模式运行 pytest --browserfirefox --headless6. 进阶技巧与常见问题排查框架搭起来只是第一步在实际项目中让它稳定运行还需要很多技巧。6.1 处理动态元素与智能等待问题有些元素是AJAX加载的出现时间不确定或者元素属性会动态变化。解决方案自定义等待条件Selenium的expected_conditions可能不够用可以自己写。# src/core/wait_conditions.py from selenium.webdriver.support.ui import WebDriverWait class element_has_css_class(object): 等待元素拥有特定的CSS类 def __init__(self, locator, css_class): self.locator locator self.css_class css_class def __call__(self, driver): element driver.find_element(*self.locator) if self.css_class in element.get_attribute(class): return element else: return False # 使用 wait.until(element_has_css_class((By.ID, “myBtn”), “active”))重试机制对于某些不稳定的操作如点击后因页面刷新导致StaleElementReferenceException可以使用重试装饰器。import time from functools import wraps def retry_on_stale_element(max_attempts3, delay1): def decorator(func): wraps(func) def wrapper(*args, **kwargs): attempts 0 while attempts max_attempts: try: return func(*args, **kwargs) except StaleElementReferenceException: attempts 1 if attempts max_attempts: raise time.sleep(delay) return None return wrapper return decorator # 在页面对象方法上使用 retry_on_stale_element() def click_submit(self): self.click(self.SUBMIT_BTN)6.2 应对反爬与检测机制问题一些网站会检测Selenium因为WebDriver会在全局变量window.navigator中留下特征如webdrivertrue。解决方案使用undetected-chromedriver这是一个第三方库专门用于修改ChromeDriver特征避免被检测。在驱动工厂中可以考虑集成它。import undetected_chromedriver as uc driver uc.Chrome()添加实验性选项旧版方法可能已失效options.add_experimental_option(“excludeSwitches”, [“enable-automation”]) options.add_experimental_option(‘useAutomationExtension’, False) options.add_argument(‘–disable-blink-featuresAutomationControlled’)重要提示请仅将此类技术用于对自己拥有权限的网站或公开的、允许自动化测试的网站进行测试。未经授权对他人网站进行自动化操作可能违反服务条款。6.3 常见错误与排查清单错误现象可能原因排查步骤与解决方案WebDriverException: Message: unknown error: cannot find Chrome binaryChrome浏览器未安装或路径不对。1. 确认系统已安装Chrome。2. 如果安装在非标准路径通过options.binary_location指定。SessionNotCreatedException: This version of ChromeDriver only supports Chrome version XX浏览器与驱动版本不匹配。使用webdriver-manager自动管理驱动这是最佳实践。NoSuchElementException: Unable to locate element元素定位器写错页面未加载完元素在iframe内。1. 用浏览器开发者工具复查定位器。2.添加显式等待等待元素出现。3. 检查是否存在iframe需要用driver.switch_to.frame()切换。ElementNotInteractableException: element not interactable元素不可交互被遮挡、未可见、禁用。1. 等待元素可见(visibility_of_element_located)。2. 检查是否有遮罩层、弹窗。3. 尝试用JavaScript直接点击driver.execute_script(“arguments[0].click();”, element)。StaleElementReferenceException之前找到的元素已不在当前DOM中页面刷新或AJAX更新。1.使用POM在每次操作前重新查找元素我们的BasePage.find_element已经做了。2. 对于连续操作同一元素使用上述重试机制。测试在本地通过在CI服务器失败CI环境如Linux Docker缺少图形界面或依赖网络、资源不同。1. 确保使用无头模式(--headless)。2. 添加Chrome启动参数--no-sandbox --disable-dev-shm-usage。3. 在CI脚本中增加必要的依赖安装步骤如字体库。4. 增加关键步骤的等待时间和日志输出。6.4 持续集成CI集成示例GitHub Actions将你的自动化测试框架集成到CI/CD流水线中实现代码提交后自动运行测试。以下是GitHub Actions的一个简单配置示例# .github/workflows/run-tests.yml name: Python Selenium Tests on: [push, pull_request] jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkoutv3 - name: Set up Python uses: actions/setup-pythonv4 with: python-version: ‘3.10’ - name: Install system dependencies (for Chrome) run: | sudo apt-get update sudo apt-get install -y wget unzip libgconf-2-4 - name: Install Chrome run: | wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | sudo apt-key add - echo deb [archamd64] http://dl.google.com/linux/chrome/deb/ stable main | sudo tee /etc/apt/sources.list.d/google-chrome.list sudo apt-get update sudo apt-get install -y google-chrome-stable - name: Install Python dependencies run: | pip install -r requirements.txt - name: Run tests with pytest and generate Allure results run: | pytest --browserchrome --headless --alluredirreports/allure-results -v - name: Upload Allure results as artifact uses: actions/upload-artifactv3 if: always() # 即使测试失败也上传结果 with: name: allure-results path: reports/allure-results/这个工作流会在每次代码推送或拉取请求时在一个干净的Ubuntu环境中安装Chrome、Python依赖然后以无头模式运行所有测试并将Allure的原始结果文件保存为制品供后续下载查看或生成报告。搭建一个健壮的Selenium自动化测试框架初期投入看似复杂但它带来的长期收益是巨大的测试用例更稳定维护成本更低团队协作更顺畅产品质量更有保障。记住框架是骨架好的测试设计和用例才是血肉。不断根据项目需求迭代你的框架补充如API测试集成、数据库断言、性能监控等模块让它真正成为支撑你们团队高质量交付的利器。