Pytest Fixture 完全指南:从零到自动化测试框架实战
在Pytest框架中fixture是一个非常重要的功能广泛应用于测试环境搭建、测试数据管理、浏览器生命周期控制以及测试报告生成等关键环节所以理解好fixture的使用对于测试框架的搭建是很有帮助的。本文将系统性地介绍Pytest fixture涵盖以下内容Pytest fixture简介解释Pytest fixture的基本概念。Fixture的作用域与执行顺序详细说明fixture的不同作用域如函数、类、模块、会话级别及其执行顺序规则自动化测试中的常见 fixture 应用场景通过实际示例展示Pytest fixture在数据准备、环境配置等方面的典型应用提升测试自动化效率。什么是Pytest fixturepytest的fixture其实是一个函数通过装饰器pytest.fixture注册到 Pytest 框架中用于在测试执行前后完成准备与清理工作。fixture的核心优势在于它的可复用性和模块化设计。你可以将常用的测试准备逻辑如数据库连接、文件读取、配置加载等封装成独立的fixture然后在测试函数中复用。此外fixture之间可以通过依赖的方式一个fixture可以依赖另一个fixture从而可以更好的管理前置处理将多个前置操作解耦。如何使用Pytest fixture当测试函数的参数名与某个fixture的函数名一致时Pytest 会自动调用该 fixture并将fixture yield的内容自动返回给参数。示例import pytest pytest.fixture def setup_and_teardown(): print([Setup] 初始化测试环境) yield fixture print([Teardown] 清理测试环境) def test_example(setup_and_teardown): print(f执行测试函数获得值{setup_and_teardown})运行结果[Setup] 初始化测试环境 执行测试函数获得值fixture [Teardown] 清理测试环境yield 前的代码在测试函数执行之前运行 这里称为setup阶段yield 后的代码在测试函数执行之后运行 这里称为teardown阶段yield 的内容会作为参数传递给使用该 fixture 的测试函数 如果通过装饰器pytest.mark.usefixtures(setup_and_teardown)引用 fixture 且未显式通过参数注入则 yield 返回的值不会被测试函数使用不影响 setup 和 teardown 的执行。Pytest fixture执行顺序Pytest fixture的执行顺序由以下三个规则决定按作用域大小从外到内作用域大的fixture如session、module先于作用域小的fixture如function执行。按依赖关系解析作用域相同时被依赖的fixture先于依赖它的fixture执行。无依赖时按参数顺序执行当多个fixture作用域相同且不存在依赖时则按在测试函数中的参数顺序决定规则1 按作用域大小从外到内在 pytest 中fixture 的执行顺序主要由其作用域决定作用域越大优先级越高。作用域从大到小依次为session → package → module → class → function。各 fixture 作用域的具体说明如下表所示。作用域执行频率使用场景function每个测试函数执行默认作用域适用于独立测试互不影响的场景class每个测试类前后执行适用于测试类内共享状态如登录信息module每个模块一个py文件前后执行适用于模块级共享资源如数据库连接package每个包前后执行Pytest 7.0新增类似于模块级别session整个测试会话前后执行适用于全局资源如 WebDriver、全局配置示例import pytest # 1. 不同作用域的fixture pytest.fixture(scopesession) def session_fixture(): print(\nSession fixture setup) yield session_data print(Session fixture teardown) pytest.fixture(scopemodule) def module_fixture(): print(Module fixture setup) yield module_data print(Module fixture teardown) pytest.fixture(scopefunction) def function_fixture(): print(Function fixture setup) yield function_data print(Function fixture teardown) # 测试函数 def test_execution_order( session_fixture, # 作用域最大最先执行 module_fixture, # 作用域次之 function_fixture, # 作用域最小 ): print(\nTest execution) assert True运行结果如下:test_scope.py::test_execution_order Session fixture setup Module fixture setup Function fixture setup PASSED [100%] Test execution Function fixture teardown Module fixture teardown Session fixture teardown从结果可以看到fixture 的调用顺序是Session → Module → Function。而 teardown 的执行顺序则与之相反。规则2: 按依赖关系解析如果fixture的作用域相同时pytest会接着根据fixture之间的互相依赖关系来决定执行顺序。在Setup阶段被依赖的fixture会先于依赖的fixture先执行例如当loginfixture依赖dbfixture则db会在login之前运行在teardown阶段时则会相反。通过依赖的方式测试框架可以方便高效的管理测试需要的前置和后置处理的执行顺序示例代码pytest.fixture def db(): print(\\n[setup] db) yield print([teardown] db) pytest.fixture def login(db): print([setup] login) yield print([teardown] login) pytest.fixture def prepare_data(login): print([setup] prepare_data) yield print([teardown] prepare_data) def test_api(prepare_data): print(执行 test_api)运行时输出结果如下tests/test_class_scope.py [setup] db [setup] login [setup] prepare_data 执行 test_api .[teardown] prepare_data [teardown] login [teardown] db根据fixture的依赖关系 prepare_data - login - db所以最后的执行顺序先执行db然后是login最后是prepare_data。测试完成后teardown代码将按照相反顺序执行。规则3无依赖时按参数顺序执行如果一个函数使用了多个fixture且前两个条件都相同作用域相同没有依赖关系。此时Pytest会依据参数的声明顺序从左到右执行。顺便一提如果你的fixture 确实存在依赖关系应显示的使用依赖机制来明确执行顺序而非依赖参数的声明顺序。示例pytest.fixture def db(): print([setup] db) yield print([teardown] db) pytest.fixture def login(): print([setup] login) yield print([teardown] login) pytest.fixture def prepare_data(): print([setup] prepare_data) yield print([teardown] prepare_data) def test_api(prepare_data, login, db): print(执行 test_api)运行后结果如下tests/test_class_scope.py [setup] prepare_data [setup] login [setup] db 执行 test_api .[teardown] db [teardown] login [teardown] prepare_data结果显示在 setup 阶段pytest会按照参数声明的顺序从左到右执行即prepare_data login db。而在teardown阶段则按照相反的顺序执行。自动化测试中常见的fixture应用场景管理浏览器实例在UI自动化测试中创建浏览器对象是必不可少的例如Selenium中的webdriver实例或Playwright中的page对象。使用Pytest fixture来管理和创建这些对象在几乎所有框架中都是一项必修课那接着展示一下如何使用pytest fixture实现通过加载配置文件进行playwright浏览器实例的创建1 配置加载:import pytest import yaml from playwright.sync_api import sync_playwright, Browser, Page import logging from typing import Dict, Any # 设置日志 logger logging.getLogger(__name__) def load_config(env: str default) - Dict[str, Any]: 从配置文件加载配置 :param env: 环境名称对应配置文件 env.{env}.yaml :return: 配置字典 config_file fconfigs/env.{env}.yaml try: with open(config_file, r, encodingutf-8) as f: config yaml.safe_load(f) logger.info(f加载配置文件: {config_file}) return config or {} except FileNotFoundError: logger.warning(f配置文件 {config_file} 不存在使用默认配置) return {} pytest.fixture(scopesession) def config(request): 加载当前运行环境配置 :param request: pytest 请求对象用于获取命令行参数 :return: 配置字典 logger.info(开始加载配置) # 从命令行参数获取环境变量如果没有则使用默认值 env request.config.getoption(--env, defaultdefault) return load_config(env)这段代码定义了一个会话级别(scope”session”) 的fixture通过load_config函数从对应的配置文件如env.dev.yaml读取配置根据命令行参数如--envdev加载对应环境的配置文件。2 浏览器实例管理browser fixturepytest.fixture(scopesession) def browser(config: Dict[str, Any]): 启动并提供一个浏览器实例测试会话结束后关闭浏览器 :param config: 配置对象 fixture - 来自 conftest.py :return: Playwright Browser 对象 logger.info(开始测试会话: 启动浏览器) playwright sync_playwright().start() # 从配置中获取浏览器参数使用默认值 browser_type config.get(browser, chromium) headless config.get(headless, True) slowmo config.get(slowmo, 0) # 额外的启动参数 launch_kwargs { headless: headless, slow_mo: slowmo } # 可选的浏览器特定参数 if browser_type chromium and config.get(chromium_args): launch_kwargs[args] config.get(chromium_args) logger.info(f启动浏览器{browser_type}, headless{headless}, slowmo{slowmo}) # 根据 browser_type 动态选择浏览器 browser getattr(playwright, browser_type).launch(**launch_kwargs) yield browser logger.info(关闭浏览器) browser_instance.close() playwright.stop()browserfixture 依赖于上一步的configfixturebrowserfixture 通过从config返回的配置中读取浏览器相关设置如 browser_type、headless 等然后根据这些配置创建并返回浏览器对象。在teardown阶段代码yield之后浏览器及 Playwright 对象都会被正确关闭避免资源泄漏。3 页面实例对象管理page fixture)pytest.fixture(scopefunction) def page(browser: Browser): 为每个测试函数提供一个 Playwright Page 对象 :param browser: 浏览器实例 fixture :return: Playwright Page 对象 # 创建新的浏览器上下文每个测试独立的会话 context browser.new_context( viewport{width: 1920, height: 1080}, ignore_https_errorsTrue # 忽略 HTTPS 错误便于测试环境 ) # 在新上下文中创建页面 page context.new_page() yield page # 测试结束后关闭上下文 logger.debug(测试结束关闭浏览器上下文) context.close()这段函数定义了一个函数级别(scope”function”) 的 fixturepage依赖于上一步的browserfixture。它为每个测试函数提供一个全新的page对象。测试数据准备测试离不开测试数据使用 Pytest的fixture 进行测试数据准备也是一种常见的方法。通过fixture管理测试数据可以有效提升测试的稳定性并确保测试之间的数据隔离避免“脏数据”对其他测试用例造成影响。举例当我们需要测试登录功能时可以在 setup 阶段调用接口预先创建一个用户然后在 teardown 阶段将其删除。import pytest def create_user(username, email): # 在实际项目中这里会调用 API 或数据库操作 user_id fuser_{username} print(f注册用户: {username} ({email})) return {id: user_id, username: username, email: email} def delete_user(user_id): # 在实际项目中这里会调用 API 或数据库操作 print(f删除用户: {user_id}) # 数据准备和清理的 Fixture pytest.fixture def registered_user(): 在测试前注册用户测试后自动清理 # 准备阶段注册用户 user create_user(test_user, testexample.com) # 将用户数据传递给测试用例 yield user # 清理阶段删除用户 delete_user(user[id]) # 测试用例 def test_user_login(registered_user): 测试用户登录功能 # 使用 fixture 提供的用户数据 assert registered_user[username] test_user assert registered_user[email] testexample.com print(f测试用户登录: {registered_user[username]})以上代码使用 registered_user fixture 来管理测试数据的初始化和清理在 setup 阶段通过 create_user 创建新用户并将 user 对象返回给测试用例在 teardown 阶段会自动清理该用户避免测试数据污染管理接口测试的认证在接口自动化测试中fixture可以用来处理认证获取token、设置统一的请求头等常见的场景示例使用fixture管理API认证Tokenimport pytest import requests import logging from typing import Dict, Any logger logging.getLogger(__name__) pytest.fixture(scopesession) def api_token() - str: 会话级别的API Token fixture - setup: 调用登录接口获取Token - yield: 返回Token供测试使用 base_url http://localhost:8080 # 可替换为 os.getenv(API_BASE_URL) login_url f{base_url}/api/login payload { username: test_user, password: test_pass123 } logger.info(开始登录获取API Token) response requests.post(login_url, jsonpayload, timeout15) response.raise_for_status() token response.json()[access_token] logger.info(f登录成功获取Token: {token[:20]}...) yield token logger.info(API Token会话结束) pytest.fixture(scopefunction) def api_headers(api_token): 函数级别的请求头fixture包含认证Token和统一头信息 依赖 api_token fixture确保每次测试都有最新头 logger.debug(准备API请求头) headers { Authorization: fBearer {api_token}, Content-Type: application/json, User-Agent: pytest-api-test/1.0 } yield headers # 测试用例 def test_get_user_profile(api_headers): 测试获取用户资料接口 base_url http://localhost:8080 # 或从config获取 url f{base_url}/api/user/profile response requests.get(url, headersapi_headers) assert response.status_code 200 user_data response.json() assert user_data[username] test_user logger.info(用户资料接口测试通过)以上示例展示了如何使用 Pytest fixture 来管理接口测试中的认证流程测试方法test_get_user_profile通过注入api_headersfixture 获取请求头api_headersfixture 调用api_tokenfixture 获取 token并将其添加到请求头的 Authorization 字段中在项目实战中可通过配置文件如 YAML或数据驱动的装饰器pytest.mark.parametrize切换测试环境与账号。总结Pytest fixture是构建可维护、模块化自动化测试框架的基础。掌握好fixture的使用不仅能让你写出更简洁的测试代码更能帮助团队建立起一套高效、稳健的自动化测试体系。通过本文我们深入了解了Pytest fixture的核心机制Pytest fixture核心概念fixture是一个通过pytest.fixture装饰器注册的函数用于在测试执行前后完成环境准备与资源清理。明确的执行顺序掌握“作用域 依赖关系 参数顺序”的优先级规则在处理复杂的前置依赖时更游刃有余。强大的实战能力无论是通过conftest.py结合配置文件动态启动浏览器还是通过fixture 实现测试数据的创建和销毁都极大地提升了测试代码的复用性和稳定性。