Python自动化测试实战:10大核心技巧构建高效测试体系

发布时间:2026/6/23 9:33:11
Python自动化测试实战:10大核心技巧构建高效测试体系 1. 项目概述为什么我们需要一本“实战宝典”在软件研发的日常里测试自动化早已不是“要不要做”的选择题而是“怎么做才能高效”的必答题。尤其是对于Python技术栈的团队从Web UI、API接口到移动端AppPython凭借其简洁的语法和丰富的生态库几乎成了自动化测试的首选语言。然而我见过太多团队和个人一上来就埋头写脚本从Selenium到Pytest从Requests到Appium代码写了一堆却发现维护成本越来越高用例执行越来越慢最终陷入“为自动化而自动化”的泥潭。问题出在哪往往不是工具不会用而是缺乏一套贯穿始终、经过实战检验的核心方法和工程化思维。这正是“实战宝典”的价值所在——它不满足于教你调用某个API而是系统地拆解如何构建一个健壮、可维护、高效率的自动化测试体系。本文将围绕10个核心技巧展开这些技巧源于我多年在多个中大型项目中踩坑、填坑的经验总结目标是让你不仅写出能跑的脚本更能打造出经得起项目迭代和时间考验的自动化资产。2. 自动化测试框架的选型与基石搭建2.1 超越 unittest为什么 Pytest 是更优的起点很多Python初学者会从内置的unittest模块开始学习自动化测试这无可厚非。但当你开始构建一个正经的项目时pytest几乎是毋庸置疑的更优选择。原因在于它的“约定优于配置”哲学和极其丰富的扩展性。首先pytest的用例编写极其简洁。它不需要你继承某个特定的类任何以test_开头的函数或方法都会被自动识别为测试用例。对比一下unittest要求你创建一个继承unittest.TestCase的类并在其中编写以test开头的方法。pytest的这种设计减少了模板代码让开发者更专注于测试逻辑本身。其次pytest的断言是原生Python的assert语句失败时会提供极其详尽的上下文信息。而unittest需要使用self.assertEqual()、self.assertTrue()等特定方法。pytest的断言在失败时能直接输出表达式中变量的值这对于调试来说是天大的福音。更重要的是pytest的插件生态。你可以通过插件轻松实现并行测试使用pytest-xdist一行命令pytest -n auto就能利用所有CPU核心并行运行用例大幅缩短测试套件的执行时间。测试报告pytest-html可以生成美观的HTML报告pytest-allure-adaptor可以集成Allure生成更强大的交互式报告。用例依赖pytest-dependency可以管理用例之间的依赖关系。参数化内置的pytest.mark.parametrize装饰器能优雅地实现数据驱动测试这是unittest需要额外费不少功夫才能实现的功能。实操心得项目初期就锁定pytest作为核心框架。不要先写一堆unittest用例再想着迁移那会是一场灾难。直接建立pytest的项目结构并尽早引入pytest.ini配置文件来管理默认的选项比如设置测试路径、添加命令行参数等。2.2 结构清晰项目目录组织的艺术一个混乱的目录结构是自动化项目后期维护的噩梦。清晰的结构不仅能提升可读性也便于CI/CD流水线的集成。我推荐以下一种经过验证的目录布局automation_framework/ ├── conftest.py # pytest共享fixture和钩子函数 ├── pytest.ini # pytest配置文件 ├── requirements.txt # 项目依赖清单 ├── common/ # 公共模块 │ ├── __init__.py │ ├── logger.py # 日志模块封装 │ ├── config_reader.py # 配置文件读取 │ └── webdriver_helper.py # 浏览器驱动管理 ├── page_objects/ # 页面对象模型 │ ├── __init__.py │ ├── base_page.py # 页面基类 │ └── login_page.py # 具体页面类 ├── test_cases/ # 测试用例 │ ├── __init__.py │ ├── test_smoke/ # 冒烟测试 │ ├── test_regression/ # 回归测试 │ └── test_api/ # API测试 ├── test_data/ # 测试数据 │ ├── users.json │ └── products.csv ├── reports/ # 测试报告.gitignore │ └── allure-results/ └── logs/ # 运行日志.gitignore └── test_run_20231027.log关键点解析conftest.py这是pytest的魔力文件。你可以在这里定义作用域scope为session、module、class、function的fixture。例如一个session级别的fixture用于启动和关闭浏览器一个function级别的fixture用于每个用例前的登录操作。它支持继承子目录下的conftest.py可以覆盖父目录的。页面对象模型Page Object Model, POM将页面的元素定位和操作封装在page_objects目录下的独立类中。测试用例只调用页面对象提供的方法不直接包含find_element等底层代码。这极大提升了代码的可维护性当页面UI变更时你只需要修改对应的页面对象类而不需要修改大量测试用例。分离测试数据将测试数据如用户名、密码、商品ID从测试逻辑中剥离存放在test_data目录的JSON、YAML或CSV文件中。用例通过数据驱动来读取使得数据调整变得非常容易也便于进行多场景测试。避坑指南reports和logs目录务必加入.gitignore避免将每次运行的动态产物提交到代码仓库。对于test_data中的敏感信息如真实数据库密码应使用环境变量或专门的 secrets 管理工具切勿硬编码或直接提交明文文件。3. 核心技巧深度解析从编写到执行3.1 技巧一善用 Fixture 实现测试生命周期管理pytest的fixture是其灵魂功能用于准备测试上下文和清理工作。理解其作用域scope是高效使用的关键。function默认每个测试函数运行一次。class每个测试类运行一次。module每个.py文件运行一次。session一次pytest执行只运行一次。一个经典的Web自动化场景如下定义在conftest.py中import pytest from selenium import webdriver from selenium.webdriver.chrome.options import Options pytest.fixture(scopesession) def browser(): 启动一个浏览器实例整个测试会话只启动一次 chrome_options Options() chrome_options.add_argument(--headless) # 无头模式适合CI环境 chrome_options.add_argument(--no-sandbox) chrome_options.add_argument(--disable-dev-shm-usage) driver webdriver.Chrome(optionschrome_options) driver.implicitly_wait(10) # 设置隐式等待 yield driver # 将driver对象提供给测试用例 # 以下是清理阶段所有用例执行完毕后运行 driver.quit() print(浏览器已关闭) pytest.fixture(scopefunction) def login(browser): 每个用例前登录返回已登录的状态 # 假设有一个登录页面对象 login_page LoginPage(browser) login_page.load() login_page.login(standard_user, secret_sauce) yield # 此处可以yield一个对象比如用户信息供用例使用 # 每个用例后可以执行登出操作如果需要 # dashboard_page.logout()在测试用例中你只需要将fixture的函数名作为参数传入即可使用def test_add_item_to_cart(browser, login): # browser 和 login fixture 已自动注入 product_page ProductPage(browser) product_page.add_item_to_cart(Sauce Labs Backpack) assert product_page.get_cart_count() 1注意事项yield是fixture实现“前置-后置”操作的关键。yield之前的代码是setup之后的代码是teardown。对于需要返回值的fixtureyield后面跟上要返回的对象。避免在fixture中做过于复杂或耗时的操作尤其是function级别的fixture否则会显著拖慢测试速度。3.2 技巧二参数化与数据驱动的艺术硬编码的测试数据是自动化脚本的“坏味道”。pytest的pytest.mark.parametrize装饰器让数据驱动变得异常优雅。基础用法import pytest pytest.mark.parametrize(username, password, expected, [ (admin, admin123, True), (admin, wrong, False), (, admin123, False), ]) def test_login(username, password, expected): result login_function(username, password) assert result expected从外部文件读取数据 对于更复杂的数据如JSON或CSV可以结合使用import json import pytest def load_test_data(): with open(test_data/login_cases.json, r) as f: return json.load(f) pytest.mark.parametrize(case, load_test_data()) def test_login_with_external_data(case): result login_function(case[username], case[password]) assert result case[expected] # 还可以断言更详细的信息如错误提示 if not case[expected]: assert get_error_message() case[error_msg]为参数化用例打标签 你可以为不同的数据组合打上不同的标记以便选择性地运行。pytest.mark.parametrize(input, expected, [ pytest.param(normal_case, success, idhappy_path), pytest.param(, error_empty, idempty_input, markspytest.mark.xfail), pytest.param(None, error_null, idnull_input, markspytest.mark.skip(Bug #123)), ]) def test_form_validation(input, expected): ...实操心得将测试数据与用例逻辑分离是迈向专业自动化的第一步。使用parametrize时为每个参数组合使用pytest.param并指定有意义的id这样在测试报告里你能清晰地看到是哪个数据组合失败了而不是一个晦涩的test_login[input_data0]。3.3 技巧三等待机制告别“NoSuchElementException”的噩梦动态加载的Web应用是自动化测试的主要挑战之一。生硬的sleep是绝对要避免的它不可靠且低效。Selenium WebDriver 提供了两种主要的智能等待方式。隐式等待Implicit Wait 在创建WebDriver后设置一次对整个Driver的生命周期有效。它告诉WebDriver在查找元素时如果元素没有立即出现可以等待一段指定的时间轮询查找。driver.implicitly_wait(10) # 单位秒缺点它是全局设置对find_element和find_elements都有效。但对于某些永远不出现的元素它仍然会等待超时拖慢整体速度。它不适用于等待元素的特定状态如可点击、可见。显式等待Explicit Wait 针对特定元素和条件进行等待更加灵活和精确。这是推荐的主要方式。from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC # 等待一个元素出现并可见 wait WebDriverWait(driver, 10) element wait.until(EC.visibility_of_element_located((By.ID, submit-button))) element.click() # 等待元素可被点击 button wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR, .btn-primary))) button.click() # 等待元素文本包含特定内容 wait.until(EC.text_to_be_present_in_element((By.TAG_NAME, h1), Welcome))最佳实践混合使用但以显式等待为主。可以设置一个较短的全局隐式等待如5秒作为“安全网”然后在关键操作前使用显式等待来等待精确的条件。同时可以封装一个自定义的等待工具函数集成日志和重试机制。避坑指南避免“隐式等待”和“显式等待”混用时间过长。例如隐式等待30秒显式等待又等10秒可能导致最坏情况下等待40秒。通常将隐式等待设置为一个较小的值2-5秒复杂的同步逻辑交给显式等待处理。此外警惕staleness_of条件它在处理页面刷新或元素重新渲染时非常有用。3.4 技巧四日志与报告让测试结果自己说话没有清晰日志和报告自动化测试就像在黑暗中航行。pytest可以轻松集成强大的日志和报告系统。结构化日志 使用Python内置的logging模块并在conftest.py或项目初始化时进行配置。import logging import sys def setup_logging(): logger logging.getLogger(__name__) logger.setLevel(logging.DEBUG) # 控制台处理器 ch logging.StreamHandler(sys.stdout) ch.setLevel(logging.INFO) # 文件处理器 fh logging.FileHandler(logs/automation.log) fh.setLevel(logging.DEBUG) formatter logging.Formatter(%(asctime)s - %(name)s - %(levelname)s - %(message)s) ch.setFormatter(formatter) fh.setFormatter(formatter) logger.addHandler(ch) logger.addHandler(fh) return logger log setup_logging()在测试用例和页面对象中使用log.info(“开始登录操作”)、log.error(“元素未找到: %s”, locator)来记录关键步骤和错误。Allure 测试报告 Allure能生成极其详细和美观的交互式报告展示用例层级、步骤、附件截图、日志、环境信息等。安装pip install allure-pytest运行测试时添加参数pytest --alluredir./reports/allure-results生成并查看报告allure serve ./reports/allure-results(需要先安装Allure命令行工具)你还可以在用例中使用Allure的装饰器来增强报告import allure allure.feature(购物车功能) allure.story(用户添加商品到购物车) def test_add_to_cart(): with allure.step(打开商品页面): ... with allure.step(点击加入购物车按钮): ... with allure.step(验证购物车数量更新): ... allure.attach(driver.get_screenshot_as_png(), name添加后页面截图, attachment_typeallure.attachment_type.PNG)注意事项日志级别要合理设置。在本地调试时可以用DEBUG在CI/CD流水线上可以设为INFO或WARNING避免日志泛滥。Allure报告非常强大但要注意不要在每次断言失败时都无差别地附加截图和大量日志这可能导致报告文件巨大。可以配置一个pytest钩子只在用例失败时自动截图并附加到Allure报告中。4. 高级实践与持续集成4.1 技巧五Page Object Model (POM) 模式的精进基础的POM是将元素定位和操作封装到类中。精进的POM则更进一步使用 LoadableComponent 模式 确保页面成功加载后再进行操作。可以在基类BasePage中实现一个is_loaded方法和load方法。class BasePage: def __init__(self, driver): self.driver driver self._verify_page() def _verify_page(self): if not self.is_loaded(): self.load() assert self.is_loaded(), fPage {self.__class__.__name__} not loaded successfully. def is_loaded(self): # 子类必须重写通过一个关键元素判断页面是否加载完成 raise NotImplementedError def load(self): # 子类必须重写定义如何导航到这个页面 raise NotImplementedError class LoginPage(BasePage): def is_loaded(self): return self.driver.find_element(By.ID, login-button).is_displayed() def load(self): self.driver.get(https://example.com/login) WebDriverWait(self.driver, 10).until(self.is_loaded)使用 Python 的property装饰器封装元素 将元素定位器封装成属性实现懒加载和缓存避免每次调用都执行find_element。class LoginPage: property def username_input(self): if not hasattr(self, _username_input): self._username_input self.driver.find_element(By.ID, user-name) return self._username_input def login(self, username, password): self.username_input.send_keys(username) # 这里才会真正查找元素 ...4.2 技巧六API 测试的利器Requests 与 Pytest 的完美结合对于后端API测试requests库是标准选择。结合pytest可以构建清晰的API测试套件。封装一个通用的API客户端import requests from requests.adapters import HTTPAdapter from urllib3.util.retry import Retry class APIClient: def __init__(self, base_url): self.base_url base_url self.session requests.Session() # 配置重试机制提高测试健壮性 retries Retry(total3, backoff_factor1, status_forcelist[502, 503, 504]) self.session.mount(http://, HTTPAdapter(max_retriesretries)) self.session.mount(https://, HTTPAdapter(max_retriesretries)) def request(self, method, endpoint, **kwargs): url f{self.base_url}{endpoint} # 可以在这里统一添加headers如认证token # kwargs[headers] {**self._default_headers, **kwargs.get(headers, {})} response self.session.request(method, url, **kwargs) # 可以在这里添加统一的响应断言或日志 log_request_and_response(response) return response def get(self, endpoint, **kwargs): return self.request(GET, endpoint, **kwargs) def post(self, endpoint, **kwargs): return self.request(POST, endpoint, **kwargs) # ... 其他HTTP方法编写API测试用例import pytest class TestUserAPI: pytest.fixture def api_client(self): return APIClient(base_urlhttps://api.example.com/v1) def test_get_user_by_id(self, api_client): response api_client.get(/users/1) assert response.status_code 200 data response.json() assert data[id] 1 assert name in data # 使用JSON Schema进行更强大的结构验证 # validate(instancedata, schemauser_schema) pytest.mark.parametrize(user_data, test_user_data) def test_create_user(self, api_client, user_data): response api_client.post(/users, jsonuser_data) assert response.status_code 201 created_user response.json() assert created_user[email] user_data[email] # 清理测试数据可选取决于测试策略 api_client.delete(f/users/{created_user[id]})4.3 技巧七集成 CI/CD让自动化测试自动运行自动化测试只有集成到CI/CD流水线中才能持续发挥价值。这里以GitHub Actions为例。创建一个.github/workflows/python-test.yml文件name: Python Automation Tests on: push: branches: [ main, develop ] pull_request: branches: [ main ] jobs: test: runs-on: ubuntu-latest strategy: matrix: python-version: [3.9, 3.10, 3.11] # 多版本Python测试 steps: - uses: actions/checkoutv3 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-pythonv4 with: python-version: ${{ matrix.python-version }} - name: Install system dependencies (for Chrome/Chromedriver) run: | sudo apt-get update sudo apt-get install -y wget unzip 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: | python -m pip install --upgrade pip pip install -r requirements.txt - name: Run tests with pytest run: | # 设置显示模式让Chrome在无头模式下运行 export DISPLAY:99 Xvfb :99 -screen 0 1920x1080x24 # 并行运行测试生成Allure结果 pytest -v -n auto --alluredir./allure-results - name: Upload Allure Report if: always() # 即使测试失败也上传报告 uses: actions/upload-artifactv3 with: name: allure-report-${{ matrix.python-version }} path: ./allure-results - name: Upload test logs if: failure() # 仅在失败时上传详细日志节省空间 uses: actions/upload-artifactv3 with: name: test-logs-${{ matrix.python-version }} path: ./logs/这个工作流实现了在代码推送或拉取请求时触发、在多版本Python环境下测试、安装浏览器依赖、并行执行测试用例、并上传测试报告和日志作为制品。4.4 技巧八测试数据管理与工厂模式测试数据的管理是另一个挑战。使用“工厂模式”例如factory_boy库可以动态生成符合业务规则的测试数据避免使用静态的、可能过期的测试数据文件。# 假设我们有一个User模型 import factory from faker import Faker fake Faker() class UserFactory(factory.Factory): class Meta: model dict # 或者你的ORM模型类如SQLAlchemy的User username factory.LazyAttribute(lambda _: fake.user_name()) email factory.LazyAttribute(lambda _: fake.email()) first_name factory.LazyAttribute(lambda _: fake.first_name()) is_active True # 在测试中使用 def test_user_creation(api_client): user_data UserFactory.build() # 构建一个字典不保存 response api_client.post(/users, jsonuser_data) assert response.status_code 201 # 或者创建一批数据 users UserFactory.build_batch(5)对于需要清理的测试数据如测试后在数据库中删除可以使用pytest的fixture配合工厂pytest.fixture def temporary_user(api_client): 创建一个临时用户测试后自动清理 user_data UserFactory.build() response api_client.post(/users, jsonuser_data) user_id response.json()[id] yield response.json() # 将创建的用户信息提供给测试用例 # 清理 api_client.delete(f/users/{user_id})4.5 技巧九性能与稳定性测试套件的优化当用例成百上千时执行速度至关重要。并行执行如前所述使用pytest-xdist(pytest -n auto)。测试分组与标记使用pytest.mark.slow、pytest.mark.integration等标记对用例进行分类。在CI中可以快速运行冒烟测试pytest -m smoke在夜间运行完整的回归套件。减少I/O和网络依赖Mock 和 Stub对于外部支付网关、短信服务、第三方API等依赖使用unittest.mock或pytest-mock进行模拟保证测试的独立性和速度。def test_payment_success(mocker, api_client): # 模拟第三方支付接口总是返回成功 mock_response mocker.Mock() mock_response.status_code 200 mock_response.json.return_value {status: success} mocker.patch(requests.post, return_valuemock_response) order_data {amount: 100} response api_client.post(/orders, jsonorder_data) assert response.status_code 201 assert response.json()[payment_status] paid使用内存数据库对于涉及数据库的测试使用SQLite内存数据库或pytest-django、pytest-flask等插件提供的测试数据库配置加速数据库操作。稳定性提升重试机制对于不稳定的测试如涉及网络微抖动可以使用pytest-rerunfailures插件对失败的用例自动重试几次pytest --reruns 3。截图与HTML转储在conftest.py中配置一个自动在用例失败时截图和保存页面源码的钩子这是定位UI测试失败原因的最有效手段之一。4.6 技巧十移动端与跨浏览器扩展测试边界Appium 移动端测试 Appium理念是“用WebDriver协议测试一切”。其配置稍复杂但模式与Selenium相似。from appium import webdriver from appium.options.common import AppiumOptions def create_android_driver(): options AppiumOptions() options.load_capabilities({ platformName: Android, appium:platformVersion: 13, appium:deviceName: Android Emulator, appium:app: /path/to/your/app.apk, appium:automationName: UiAutomator2, appium:noReset: False }) driver webdriver.Remote(http://localhost:4723, optionsoptions) return driver同样在移动端强烈推荐使用POM模式来封装页面和操作。Selenium Grid 跨浏览器测试 为了确保网站在Chrome、Firefox、Safari等浏览器上表现一致可以使用Selenium Grid。搭建一个Hub和多个Node或使用云服务如BrowserStack、Sauce Labs。在测试中通过DesiredCapabilities指定浏览器和版本。使用pytest的参数化功能轻松运行同一套用例在不同浏览器上。import pytest from selenium import webdriver from selenium.webdriver import Remote pytest.fixture(params[chrome, firefox, edge]) def driver(request): if request.param chrome: options webdriver.ChromeOptions() caps options.to_capabilities() elif request.param firefox: options webdriver.FirefoxOptions() caps options.to_capabilities() # ... 其他浏览器配置 # 连接到Selenium Grid Hub driver Remote(command_executorhttp://grid-hub:4444/wd/hub, desired_capabilitiescaps) yield driver driver.quit() def test_example(driver): driver.get(https://www.example.com) assert Example in driver.title5. 常见问题与排查技巧实录即使掌握了所有技巧在实际运行中依然会遇到各种“坑”。这里记录一些高频问题和解决思路。问题现象可能原因排查步骤与解决方案ElementNotInteractableException或ElementClickInterceptedException元素被遮挡、未处于可交互状态如不可见、disabled、有弹窗覆盖。1. 增加显式等待等待元素element_to_be_clickable。2. 使用driver.execute_script(“arguments[0].scrollIntoView();”, element)滚动到元素可见区域。3. 使用driver.execute_script(“arguments[0].click();”, element)进行JS点击绕过前端事件拦截。4. 检查是否有模态框Modal、Cookie提示等遮挡先关闭它们。NoSuchElementException元素定位器错误、页面未加载完成、元素在iframe或shadow DOM内。1.首要检查用例失败时自动截图和保存页面源码这是最直接的证据。2. 使用浏览器开发者工具F12的Console输入$$(“你的CSS选择器”)或$x(“你的XPath”)验证定位器是否正确。3. 增加等待时间确保元素加载出来。4. 检查元素是否在iframe中需要使用driver.switch_to.frame()切换上下文。5. 对于Shadow DOM需要使用driver.execute_script通过shadowRoot来查找元素。测试在本地通过在CI上失败环境差异浏览器版本、驱动版本、屏幕分辨率、时区、无头模式、资源加载超时、网络延迟。1.固定版本在CI配置中明确指定Chrome/Chromedriver的版本与本地一致。2.增加超时适当增加隐式/显式等待时间CI环境可能比本地慢。3.检查无头模式有些前端逻辑在无头模式下行为不同可以尝试在CI中先不使用--headless运行一次或添加特定的无头模式参数如--disable-gpu,--window-size1920,1080。4.查看CI日志和制品仔细阅读CI运行的输出日志下载失败时生成的截图和HTML进行分析。测试执行速度越来越慢用例之间存在依赖或副作用、未及时清理测试数据、单个fixture作用域过大、网络请求或数据库查询未优化。1.用例独立确保每个测试用例都是独立的不依赖其他用例的执行状态。使用setup_method/teardown_method或fixture清理环境。2.优化fixture作用域将session或module级别的fixture拆解能function级别的就不要用更大的作用域。3.使用Mock将对外部慢服务的调用替换为Mock。4.启用并行使用pytest-xdist。5.分析耗时使用pytest --durations10找出最慢的10个测试用例进行优化。随机性失败Flaky Tests异步操作未同步、时间相关测试、竞态条件、第三方服务不稳定。1.强化等待使用更精确的显式等待条件而不是固定sleep。2.重试机制对不稳定的测试用例或整个套件使用pytest-rerunfailures。3.移除时间依赖避免测试依赖于具体时间如“今天”使用Mock或固定时间。4.隔离外部依赖对网络、数据库等外部依赖进行Mock或使用测试替身。一个实用的调试技巧在conftest.py中添加一个自动截图钩子。import pytest from selenium import webdriver pytest.hookimpl(hookwrapperTrue) def pytest_runtest_makereport(item, call): 在每个测试执行后制作报告如果失败则截图。 outcome yield report outcome.get_result() if report.when call and report.failed: # 假设driver fixture的名字是browser driver_fixture item.funcargs.get(browser) if driver_fixture and isinstance(driver_fixture, webdriver.Remote): try: screenshot driver_fixture.get_screenshot_as_base64() # 可以附加到Allure报告或保存到文件 allure.attach(driver_fixture.get_screenshot_as_png(), namefailure_screenshot, attachment_typeallure.attachment_type.PNG) # 或者保存到指定目录 screenshot_dir ./screenshots os.makedirs(screenshot_dir, exist_okTrue) filename f{item.name}_{datetime.now().strftime(%Y%m%d_%H%M%S)}.png driver_fixture.save_screenshot(os.path.join(screenshot_dir, filename)) except Exception as e: print(fFailed to take screenshot: {e})掌握这十大核心技巧并深入理解其背后的原理和最佳实践你构建的将不再是一堆脆弱的脚本而是一个坚实、高效、可维护的自动化测试工程。自动化测试的终极目标不是替代手工测试而是通过机器的不懈执行将测试人员从重复劳动中解放出来让他们有更多精力去从事探索性测试、用户体验评估等更具创造性的工作。从选择一个合适的框架开始一步步搭建你的测试大厦每一次迭代都让它更稳固、更智能。