Python+pytest+Selenium构建可维护UI自动化测试框架实战
1. 项目概述:为什么选择 Python + pytest 做 UI 自动化?
如果你正在为重复的手工点击、繁琐的回归测试而头疼,或者团队里还在用着维护成本高、可读性差的“脚本堆”,那么今天聊的这个组合,绝对值得你花时间研究一下。我用了快十年的 Python 做自动化,从早期的 unittest 到 nose,再到现在的 pytest,结合 Selenium 做 UI 自动化,这套技术栈已经成了我们团队的核心生产力工具。它解决的不仅仅是“自动化”的问题,更是如何让自动化测试变得 可持续、易维护、好协作 。
简单来说,“Python 结合 pytest 进行 UI 自动化测试”就是用 Python 这门简洁高效的语言,驱动浏览器(通过 Selenium 等库)模拟用户操作,并利用 pytest 这个强大的测试框架来组织、运行和管理这些测试用例。它的核心价值在于: 用极低的代码量实现复杂的测试逻辑,用清晰的规则管理测试生命周期,用丰富的插件生态生成漂亮的报告和实现高级功能 。无论是测试一个简单的登录页面,还是一个拥有上百个功能模块的复杂 Web 应用,这套组合都能游刃有余。
适合谁来学?如果你是测试工程师,想从手工测试转向自动化;如果你是开发工程师,想为自己的项目补充前端自动化测试;甚至你是团队负责人,想引入一套标准化的自动化方案来提升交付质量,这篇文章都能给你一套从零搭建到实战优化的完整思路。接下来,我会带你深入这套组合的每一个核心环节,拆解其设计思想,并分享大量我在实际项目中踩过的坑和总结的技巧。
2. 核心框架设计与选型逻辑
在动手写代码之前,理清框架的设计思路至关重要。一个混乱的自动化项目,其维护成本很快就会超过它带来的收益。我们选择 Python + pytest + Selenium,背后有一整套经过实践检验的考量。
2.1 为什么是 Python?
首先,语言选型上,Python 几乎是自动化测试领域的“普通话”。其语法简洁,学习曲线平缓,能让测试人员快速上手,将精力集中在测试逻辑而非语言特性上。庞大的第三方库生态(PyPI)意味着你几乎能找到任何需要的工具,从 Web 驱动(Selenium)、API 测试(requests)、到移动端(Appium)、RPA(pyautogui)都能覆盖。这对于需要多技术栈融合的测试场景非常友好。此外,Python 与 DevOps 工具链(如 Jenkins, GitLab CI)的集成也异常顺畅。
2.2 为什么是 pytest 而非 unittest?
这是很多新手会困惑的点。Python 自带 unittest 模块,为何还要用 pytest?关键在于 “约定优于配置” 和 “极强的扩展性” 。
- 更简洁的语法 :pytest 不需要你继承某个特定的类,任何以
test_开头的函数或方法都会被自动识别为测试用例。断言也直接用 Python 原生的assert语句,失败时 pytest 会给出极其详细的差异对比,这比 unittest 的self.assertEqual()直观太多。 - 强大的 Fixture 机制 :这是 pytest 的“灵魂”。Fixture 可以理解为测试的“脚手架”,用于提供测试所需的数据、环境或资源(如浏览器驱动实例),并能精确控制其生命周期(函数级、类级、模块级、会话级)。这完美解决了 UI 自动化中浏览器初始化、登录态管理、数据清理等繁琐问题。相比之下,unittest 的
setUp/tearDown显得笨重且不够灵活。 - 丰富的插件生态 :你需要生成 HTML 报告?有
pytest-html。需要更炫酷的 Allure 报告?有pytest-allure。需要控制用例执行顺序、分组、跳过、参数化?pytest 都原生支持或有成熟插件。这种“乐高积木”式的扩展能力,让框架能轻松适应不同项目的定制化需求。 - 优秀的命令行工具 :可以非常方便地选择运行某个目录、某个文件、某个类、甚至某个标记(mark)的用例,便于快速调试和分模块测试。
2.3 为什么结合 Selenium?
对于 Web UI 自动化,Selenium 是事实上的标准。它支持所有主流浏览器(Chrome, Firefox, Edge, Safari),提供了完整的浏览器操控 API。更重要的是,它的 WebDriver 协议是 W3C 标准,这意味着其生态稳定,社区活跃,遇到问题很容易找到解决方案。虽然也有 Playwright、Cypress 等后起之秀,但 Selenium 的普适性和成熟度,对于大多数企业级项目来说依然是稳妥的首选。
2.4 核心架构:Page Object Model (POM)
直接在被测页面上写定位和操作代码是自动化项目走向混乱的开端。我们必须采用 Page Object Model (页面对象模型) 设计模式。它的核心思想是将 页面元素定位 与 业务操作逻辑 分离,将 测试用例 与 页面对象 分离。
一个典型的三层 POM 架构如下:
- 基础层 (BasePage) :封装所有 Selenium 的底层操作,如查找元素、点击、输入、等待等。所有页面类都继承自此基类。
- 页面对象层 (Page Objects) :每个页面对应一个类。这个类里只包含该页面的元素定位符(如
username_input = (By.ID, ‘username’))和在这个页面上的操作(如login(username, password))。 绝不包含断言 。 - 测试用例层 (Test Cases) :这里才是真正的测试逻辑。调用页面对象的方法来完成业务流程,并在此处进行断言(验证结果)。
这样做的好处是:当页面 UI 发生变更时,你只需要去对应的页面对象类里修改元素定位符,所有引用该元素的测试用例都无需改动,极大提升了可维护性。测试用例读起来就像自然语言,清晰易懂。
实操心得 :在小型或中期项目中,我强烈建议将元素定位信息(如
id,xpath)从 Python 代码中剥离出来,存入YAML或JSON文件。这样,即使不懂代码的团队成员(如产品、设计)也能在配置文件中维护定位符,实现更彻底的“测开分离”。上文示例中的readelement.py和click.yaml就是这种思路的体现。
3. 环境搭建与核心工具链详解
光说不练假把式,我们一步步把环境搭起来。这里我会给出详细的步骤和每个步骤背后的原因。
3.1 Python 环境与项目管理
首先,不要使用系统自带的 Python。推荐使用 Miniconda 或 pyenv 来创建独立的虚拟环境。这能避免项目间的包版本冲突。
# 1. 安装 Miniconda (略过)
# 2. 创建并激活一个专用于自动化测试的虚拟环境
conda create -n ui_auto python=3.8 -y
conda activate ui_auto
# 3. 在项目根目录初始化 pip 和创建 requirements.txt
pip install --upgrade pip
在你的项目根目录创建 requirements.txt 文件,这是管理依赖的标准做法:
# requirements.txt
selenium==4.15.0
pytest==7.4.3
pytest-html==4.0.2
pytest-xdist==3.5.0
allure-pytest==2.13.2
PyYAML==6.0.1
webdriver-manager==4.0.1
然后一键安装:
pip install -r requirements.txt
-
selenium: Web 自动化核心库。 -
pytest: 测试框架本体。 -
pytest-html: 生成简洁的 HTML 报告。 -
pytest-xdist: 实现测试用例并行执行,加速测试过程。 -
allure-pytest: 集成 Allure,生成非常强大和美观的交互式测试报告。 -
PyYAML: 用于读写 YAML 格式的配置文件(如元素定位文件)。 -
webdriver-manager: 强烈推荐! 它自动下载和管理浏览器驱动(ChromeDriver, GeckoDriver等),省去手动下载和配置 PATH 的麻烦。
3.2 浏览器驱动与 WebDriver Manager
过去,我们需要根据浏览器版本去官网下载对应版本的驱动,并设置系统路径。现在,用 webdriver-manager 可以完全自动化这个过程。
# 示例:使用 webdriver-manager 初始化 Chrome 驱动
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager
from selenium.webdriver.chrome.options import Options
def get_chrome_driver():
chrome_options = Options()
# 常用配置
chrome_options.add_argument('--start-maximized') # 启动最大化
chrome_options.add_argument('--disable-gpu') # 禁用GPU加速,规避一些潜在问题
chrome_options.add_argument('--no-sandbox') # 在CI环境(如Docker)中可能需要
chrome_options.add_experimental_option('excludeSwitches', ['enable-logging']) # 禁用控制台无关日志
# 关键!使用 webdriver-manager 自动获取驱动
service = Service(ChromeDriverManager().install())
driver = webdriver.Chrome(service=service, options=chrome_options)
return driver
这样,无论团队成员或 CI 服务器上的 Chrome 版本如何,代码都能自动适配正确的驱动,极大降低了环境配置成本。
3.3 项目目录结构规划
一个清晰的目录结构是项目可维护性的基石。参考之前给出的示例,并结合最佳实践,我推荐如下结构:
ui_auto_project/
├── conftest.py # pytest 全局配置,存放全局 fixture
├── pytest.ini # pytest 配置文件
├── requirements.txt # 项目依赖
├── main.py # 测试执行入口(可选)
├── common/ # 通用模块
│ ├── __init__.py
│ ├── read_config.py # 读取配置文件
│ └── readelement.py # 读取元素定位文件
├── config/ # 配置目录
│ ├── __init__.py
│ ├── conf.py # 配置常量(项目路径、定位模式映射等)
│ └── config.ini # 配置文件(数据库、URL等)
├── page/ # 页面对象基类
│ ├── __init__.py
│ └── webpage.py # 封装 Selenium 基础操作的 BasePage
├── page_element/ # 存放页面元素定位的 YAML 文件
│ ├── login_page.yaml
│ └── home_page.yaml
├── page_object/ # 具体的页面对象类
│ ├── __init__.py
│ ├── login_page.py
│ └── home_page.py
├── testcases/ # 测试用例层
│ ├── __init__.py
│ ├── test_login.py
│ └── test_order.py
├── reports/ # 测试报告输出目录
│ ├── html/
│ └── allure/
├── logs/ # 日志文件目录
├── utils/ # 工具函数
│ ├── __init__.py
│ ├── logger.py # 日志模块
│ └── times.py # 时间处理工具
└── data/ # 测试数据文件(如 JSON, CSV)
└── test_data.json
这个结构体现了清晰的关注点分离,新人加入项目也能快速找到对应模块进行修改。
4. 核心代码实现与 POM 模式深度解析
现在,我们深入到每一层,看看代码具体怎么写。我会用登录功能作为贯穿始终的例子。
4.1 基础层 (BasePage) 封装
page/webpage.py 是我们的基石。它的目标是封装所有与 Selenium 直接交互的、不涉及具体业务的底层操作,并提供健壮的等待和异常处理。
# webpage.py
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.common.exceptions import TimeoutException, NoSuchElementException
from config.conf import cm # 导入配置管理器
import logging
class WebPage:
"""Selenium 操作基类"""
def __init__(self, driver):
self.driver = driver
self.logger = logging.getLogger(__name__)
# 设置显式等待超时时间
self.timeout = 20
self.wait = WebDriverWait(self.driver, self.timeout)
def find_element(self, locator):
"""
查找单个元素(核心方法)
:param locator: 元组,如 ('id', 'username') 或从YAML读取的 ('xpath', '//button')
:return: WebElement 对象
"""
try:
# 使用显式等待,直到元素出现在DOM中
element = self.wait.until(EC.presence_of_element_located(locator))
self.logger.debug(f"成功定位元素: {locator}")
return element
except TimeoutException:
self.logger.error(f"定位元素超时: {locator}")
# 这里可以附加截图,方便调试
self.save_screenshot(f"element_not_found_{locator[0]}_{locator[1]}")
raise
def find_elements(self, locator):
"""查找多个元素"""
try:
elements = self.wait.until(EC.presence_of_all_elements_located(locator))
return elements
except TimeoutException:
self.logger.warning(f"定位一组元素未找到: {locator}")
return [] # 返回空列表,避免用例因找不到元素而中断,具体看业务场景
def click(self, locator):
"""点击元素"""
element = self.find_element(locator)
try:
# 先尝试等待元素可点击
clickable_element = WebDriverWait(self.driver, 10).until(
EC.element_to_be_clickable(locator)
)
clickable_element.click()
self.logger.info(f"点击元素: {locator}")
except Exception as e:
self.logger.error(f"点击元素失败 {locator}: {e}")
raise
def input_text(self, locator, text):
"""输入文本,输入前先清空"""
element = self.find_element(locator)
element.clear()
element.send_keys(text)
self.logger.info(f"在元素 {locator} 中输入文本: {text}")
def get_text(self, locator):
"""获取元素文本"""
element = self.find_element(locator)
return element.text
def save_screenshot(self, name):
"""保存截图,用于失败调试"""
import datetime
timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
filepath = f"./screenshots/{name}_{timestamp}.png"
self.driver.save_screenshot(filepath)
self.logger.info(f"截图已保存至: {filepath}")
return filepath
# ... 其他通用方法,如切换窗口/iframe、执行JS等
注意事项 :
find_element方法中我使用了EC.presence_of_element_located,它只要求元素存在于 DOM 中,不一定可见或可点击。对于点击操作,在click方法里我额外使用了EC.element_to_be_clickable等待,这更符合实际交互逻辑,能有效规避因元素未完全渲染导致的点击失败。
4.2 元素定位与数据驱动
将元素定位信息从代码中分离是专业化的标志。我们使用 YAML 文件。
首先,在 config/conf.py 中定义定位模式映射和路径:
# conf.py
import os
from selenium.webdriver.common.by import By
class ConfigManager:
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
ELEMENT_PATH = os.path.join(BASE_DIR, 'page_element')
LOCATE_MODE = {
'id': By.ID,
'name': By.NAME,
'class': By.CLASS_NAME,
'tag': By.TAG_NAME,
'link': By.LINK_TEXT,
'partial_link': By.PARTIAL_LINK_TEXT,
'xpath': By.XPATH,
'css': By.CSS_SELECTOR
}
cm = ConfigManager()
然后,创建 common/readelement.py 来读取 YAML:
# readelement.py
import os
import yaml
from config.conf import cm
class Element:
def __init__(self, page_name):
self.file_name = f'{page_name}.yaml'
self.element_path = os.path.join(cm.ELEMENT_PATH, self.file_name)
if not os.path.exists(self.element_path):
raise FileNotFoundError(f"元素文件 {self.file_name} 不存在!")
with open(self.element_path, 'r', encoding='utf-8') as f:
self.data = yaml.safe_load(f)
def __getitem__(self, key):
"""通过键名获取定位符元组,如 login['username_input']"""
locator_str = self.data.get(key)
if not locator_str:
raise KeyError(f"在文件 {self.file_name} 中未找到键: {key}")
# 假设YAML中格式为: `username_input: "id==username"`
locate_method, value = locator_str.split('==')
if locate_method not in cm.LOCATE_MODE:
raise ValueError(f"不支持的定位方式: {locate_method}")
return (cm.LOCATE_MODE[locate_method], value)
接着,在 page_element/login_page.yaml 中定义元素:
# login_page.yaml
username_input: "id==username"
password_input: "name==password"
login_button: "css==button[type='submit']"
error_message: "class==alert-error"
4.3 页面对象层实现
现在,创建具体的页面对象类 page_object/login_page.py :
# login_page.py
from page.webpage import WebPage
from common.readelement import Element
import allure
class LoginPage(WebPage):
def __init__(self, driver):
super().__init__(driver)
self.element = Element('login_page') # 加载 login_page.yaml
@allure.step("输入用户名")
def input_username(self, username):
self.input_text(self.element['username_input'], username)
@allure.step("输入密码")
def input_password(self, password):
self.input_text(self.element['password_input'], password)
@allure.step("点击登录按钮")
def click_login_button(self):
self.click(self.element['login_button'])
@allure.step("执行登录流程")
def login(self, username, password):
self.input_username(username)
self.input_password(password)
self.click_login_button()
@allure.step("获取错误提示信息")
def get_error_message(self):
return self.get_text(self.element['error_message'])
注意 @allure.step 装饰器,它会在 Allure 报告中为每个步骤生成一个可折叠的节点,让报告更清晰。
4.4 测试用例层与 pytest Fixture
测试用例应该只关心业务流和断言。所有环境准备和清理工作交给 conftest.py 中的 fixture。
首先,创建全局的 conftest.py :
# conftest.py
import pytest
from selenium import webdriver
from webdriver_manager.chrome import ChromeDriverManager
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.chrome.options import Options
@pytest.fixture(scope="session")
def driver():
"""会话级别的 fixture,整个测试会话只启动一次浏览器"""
chrome_options = Options()
chrome_options.add_argument('--start-maximized')
chrome_options.add_argument('--disable-gpu')
# 无头模式选项,用于CI环境
# if os.getenv('RUN_IN_CI'):
# chrome_options.add_argument('--headless')
# chrome_options.add_argument('--no-sandbox')
# chrome_options.add_argument('--disable-dev-shm-usage')
service = Service(ChromeDriverManager().install())
_driver = webdriver.Chrome(service=service, options=chrome_options)
_driver.implicitly_wait(10) # 设置全局隐式等待
yield _driver # 测试用例执行时使用这个 driver 实例
# 所有测试结束后执行清理
_driver.quit()
print("测试结束,浏览器已关闭。")
@pytest.fixture(scope="function")
def login_page(driver):
"""函数级别的 fixture,每个测试函数都会得到一个全新的页面对象实例"""
from page_object.login_page import LoginPage
page = LoginPage(driver)
page.driver.get("https://your-test-app.com/login") # 打开登录页
yield page
# 每个用例后可以清理cookie或刷新页面,保持独立
# driver.delete_all_cookies()
然后,编写测试用例 testcases/test_login.py :
# test_login.py
import allure
import pytest
from page_object.login_page import LoginPage
class TestLogin:
"""登录功能测试"""
@allure.feature("登录功能")
@allure.story("成功登录")
def test_login_success(self, login_page):
"""测试使用正确用户名密码登录成功"""
login_page.login(username="correct_user", password="correct_pass")
# 断言:登录后应跳转到首页,通过URL或页面特定元素判断
assert login_page.driver.current_url == "https://your-test-app.com/home"
# 或者断言首页的欢迎语出现
# assert login_page.get_text(welcome_locator) == "Welcome, correct_user!"
@allure.feature("登录功能")
@allure.story("登录失败-用户名错误")
@pytest.mark.parametrize("username, password, expected_error", [
("wrong_user", "correct_pass", "用户名或密码错误"),
("", "correct_pass", "用户名不能为空"),
("correct_user", "", "密码不能为空"),
])
def test_login_failure(self, login_page, username, password, expected_error):
"""参数化测试多种登录失败场景"""
login_page.login(username=username, password=password)
# 断言错误信息符合预期
actual_error = login_page.get_error_message()
assert expected_error in actual_error, f"期望错误信息包含'{expected_error}',实际为'{actual_error}'"
这里展示了 pytest 强大的参数化功能 ( @pytest.mark.parametrize ),可以用一组数据驱动多个测试场景,避免写重复代码。
5. 测试执行、报告生成与高级技巧
框架搭好了,用例写好了,接下来就是如何高效地运行它们并得到直观的反馈。
5.1 使用 pytest 命令行执行测试
pytest 提供了极其灵活的执行方式。
# 1. 运行所有测试
pytest
# 2. 运行指定目录下的测试
pytest testcases/
# 3. 运行指定文件中的测试
pytest testcases/test_login.py
# 4. 运行指定类或方法
pytest testcases/test_login.py::TestLogin
pytest testcases/test_login.py::TestLogin::test_login_success
# 5. 使用标记 (mark) 运行特定测试
# 先在用例上打标记:@pytest.mark.smoke
pytest -m smoke
# 6. 并行运行测试,加速执行 (需要 pytest-xdist)
pytest -n auto # auto 表示使用与CPU核心数相同的worker
# 7. 生成简易HTML报告 (需要 pytest-html)
pytest --html=reports/html/report.html --self-contained-html
# 8. 生成 Allure 结果数据
pytest --alluredir=reports/allure-results
5.2 生成 Allure 测试报告
Allure 报告是提升测试结果可读性和团队协作效率的神器。生成报告需要两步:
- 运行测试并生成结果数据 :如上所述,使用
--alluredir参数。 - 将结果数据生成为 HTML 报告 :
# 方式一:使用命令行(需先安装Allure命令行工具) allure generate reports/allure-results -o reports/allure-report --clean allure open reports/allure-report # 在浏览器中打开报告 # 方式二:在Python代码中调用(推荐,便于集成) import os os.system('allure generate reports/allure-results -o reports/allure-report --clean')
Allure 报告会展示清晰的测试套件结构、通过/失败/跳过用例的统计、详细的步骤日志、截图、以及历史趋势图,非常专业。
5.3 常见问题与排查技巧实录
在实际项目中,你会遇到各种各样的问题。这里记录一些高频问题和我的解决思路。
问题1:元素定位不到,报 NoSuchElementException 或 TimeoutException 。
- 可能原因及排查 :
- 等待时间不足 :UI 加载需要时间。优先使用显式等待 (
WebDriverWait),而非硬性等待 (time.sleep) 或仅依赖隐式等待。 - 定位符错误或已变更 :使用浏览器开发者工具(F12)的
Console选项卡,输入$$(“你的css选择器”)或$x(“你的xpath”)验证是否能找到元素。 XPath 尽量使用相对路径,避免使用绝对路径和依赖不稳定的属性(如自动生成的id)。 - 元素在 iframe 或 shadow DOM 中 :需要先切换到对应的 iframe (
driver.switch_to.frame) 或使用shadow_root属性访问。 - 页面未加载完全 :在操作前等待某个关键元素出现,作为页面加载完成的标志。
- 浏览器窗口未最大化 :某些响应式页面元素在窗口较小时可能被隐藏或布局改变。在 fixture 中设置
driver.maximize_window()。
- 等待时间不足 :UI 加载需要时间。优先使用显式等待 (
问题2:测试在本地通过,但在 CI(如 Jenkins)上失败。
- 可能原因及排查 :
- 无头模式 (Headless) 差异 :CI 环境通常以无头模式运行浏览器。一些动画或懒加载在无头模式下行为可能不同。在 Chrome 无头模式选项中添加
--window-size=1920,1080并确保使用了合适的等待。 - 环境隔离 :CI 环境是干净的,可能缺少 cookies、localStorage 等登录态。需要在测试开始前通过 API 或 UI 完成登录,并将登录态(cookies)保存和复用。上文
conftest.py示例中向浏览器添加 cookies 就是解决此问题。 - 资源竞争与并发 :如果并行执行测试,确保测试用例之间是隔离的(使用独立的用户账号、测试数据),避免因共享数据导致冲突。使用
pytest-xdist的--dist=loadscope参数可以尝试将同一个模块的测试分配给同一个 worker,减少资源竞争。
- 无头模式 (Headless) 差异 :CI 环境通常以无头模式运行浏览器。一些动画或懒加载在无头模式下行为可能不同。在 Chrome 无头模式选项中添加
问题3:测试执行速度慢。
- 优化策略 :
- 并行执行 :使用
pytest-xdist。 - 优化等待策略 :减少硬性等待 (
time.sleep),多用显式等待,并设置合理的超时时间。 - 复用浏览器会话 :使用
scope=’session’的 fixture 来初始化一次浏览器,所有测试共用。但要注意测试间的清理(如登出、清理 cookies),防止状态污染。 - 启用缓存 :对于从网络下载的静态资源,可以配置浏览器使用缓存。
- 选择更快的定位器 :通常
id>name>css>xpath。xpath在复杂文档中遍历可能较慢。
- 并行执行 :使用
问题4:测试报告中没有截图或截图不清晰。
- 解决方案 :在
conftest.py中编写一个自动截图 fixture,并将其与 pytest 的钩子函数结合。
这样,每当测试失败时,会自动截取当前浏览器页面并附加到报告中。# conftest.py import pytest from selenium import webdriver @pytest.hookimpl(tryfirst=True, hookwrapper=True) def pytest_runtest_makereport(item, call): """ 获取每个测试用例执行结果的钩子函数 """ outcome = yield rep = outcome.get_result() # 只关注用例执行阶段(setup, call, teardown)中的call,即测试步骤本身 if rep.when == "call" and rep.failed: # 获取测试用例中的driver fixture for name, fixtureinfo in item._fixtureinfo.name2fixturedefs.items(): if name == "driver": driver = item.funcargs[name] # 调用之前封装好的截图方法 screenshot_path = driver.save_screenshot(f"failure_{item.name}") # 将截图附件添加到Allure报告(如果使用Allure) if hasattr(rep, 'extra'): import allure allure.attach.file(screenshot_path, name="失败截图", attachment_type=allure.attachment_type.PNG) break
问题5:如何管理不同的测试环境(测试/预发/生产)?
- 最佳实践 :使用配置文件和环境变量。在
config.ini或config.yaml中定义不同环境的 URL、数据库连接等信息。通过环境变量(如ENV=testing)决定加载哪套配置。# config/conf.py import os from configparser import ConfigParser env = os.getenv('ENV', 'testing').upper() # 默认为测试环境 config = ConfigParser() config.read('config/config.ini') BASE_URL = config.get(env, 'base_url') DB_HOST = config.get(env, 'db_host')
坚持记录这些问题的解决方案,并形成团队的“知识库”,能极大提升后续的排查效率。UI 自动化测试是一个需要持续投入和优化的工程,初期搭建框架会花些时间,但一旦体系建立起来,它所带来的回归测试效率提升和产品质量保障将是巨大的。
更多推荐

所有评论(0)