Pytest Fixture 详解

Pytest Fixture 详解

Fixture 是 pytest 最强大的功能之一,用于提供测试所需的依赖资源(如数据库连接、临时文件、模拟对象等),并支持复用、作用域控制和自动清理。以下是全面详解:


1. 基本用法

定义 Fixture

使用 @pytest.fixture 装饰器定义:

python 复制代码
import pytest

@pytest.fixture
def database_connection():
    conn = create_db_connection()  # 初始化资源
    yield conn                     # 返回资源,测试结束后执行清理
    conn.close()                   # 清理操作(yield 之后的部分)

使用 Fixture

在测试函数中作为参数传入:

python 复制代码
def test_query_data(database_connection):
    result = database_connection.execute("SELECT * FROM users")
    assert len(result) > 0

2. Fixture 作用域(Scope)

通过 scope 参数控制 Fixture 的生命周期:

作用域 说明 使用场景
function 每个测试函数运行一次(默认) 轻量级资源(如临时变量)
class 每个测试类运行一次 类共享资源(如配置对象)
module 每个测试模块(文件)运行一次 全局配置(如日志初始化)
package 每个测试包运行一次 包级共享资源
session 整个测试会话只运行一次 昂贵资源(如数据库连接池)

示例:

python 复制代码
@pytest.fixture(scope="module")
def shared_config():
    return {"timeout": 30}

3. Fixture 的 Setup/Teardown

使用 yield 或 addfinalizer 实现资源清理:

yield 方式(推荐)

python 复制代码
@pytest.fixture
def temp_file():
    file = open("/tmp/test.txt", "w")
    yield file  # 测试中使用 file 对象
    file.close()  # 测试结束后关闭文件

addfinalizer 方式(更灵活)

python 复制代码
@pytest.fixture
def temp_file(request):
    file = open("/tmp/test.txt", "w")
    def cleanup():
        file.close()
    request.addfinalizer(cleanup)  # 注册清理函数
    return file

4. Fixture 参数化

用 @pytest.mark.parametrize 对 Fixture 参数化:

python 复制代码
@pytest.fixture(params=["user1", "user2", "admin"])
def user(request):
    return create_user(request.param)  # 根据参数生成不同用户

def test_user_permissions(user):
    assert user.has_permission("read")

5. 自动使用 Fixture(autouse)

设置 autouse=True,无需显式调用即可自动运行:

python 复制代码
@pytest.fixture(autouse=True)
def setup_logging():
    logging.basicConfig(level=logging.INFO)  # 所有测试自动启用日志

def test_example():
    assert True  # 此测试会自动执行 setup_logging

6. Fixture 依赖注入

Fixture 可以依赖其他 Fixture:

python 复制代码
@pytest.fixture
def db():
    return Database()

@pytest.fixture
def api_client(db):  # 依赖 db fixture
    return APIClient(db)

def test_api(api_client):
    response = api_client.get("/users")
    assert response.status_code == 200

7. 动态 Fixture

通过 request 对象动态调整 Fixture:

python 复制代码
@pytest.fixture
def dynamic_data(request):
    marker = request.node.get_closest_marker("data")
    if marker:
        return marker.args[0]  # 获取测试标记的参数
    return {"default": 42}

@pytest.mark.data({"custom": 100})
def test_dynamic(dynamic_data):
    assert dynamic_data["custom"] == 100

8. 内置 Fixture

pytest 提供了一些常用内置 Fixture:

Fixture 说明
tmp_path 临时目录路径(pathlib.Path)
tmpdir 临时目录(Legacy py.path.local)
capsys 捕获 stdout/stderr
monkeypatch 动态修改对象或环境变量
request 访问测试上下文(如参数、标记)

示例:

python 复制代码
def test_tmp_file(tmp_path):
    file = tmp_path / "test.txt"
    file.write_text("Hello")
    assert file.read_text() == "Hello"

9. Fixture 覆盖与插件

覆盖 Fixture

在 conftest.py 中定义的 Fixture 可被当前目录及子目录的测试共享:

复制代码
project/
├── conftest.py         # 定义全局 Fixture
├── tests/
│   ├── conftest.py     # 覆盖父级 Fixture
│   └── test_demo.py

Fixture 插件

通过插件扩展 Fixture(如 pytest-django 提供 django_db):

python 复制代码
def test_django_model(django_db):
    user = User.objects.create(name="Alice")
    assert user.name == "Alice"

10. 最佳实践

  1. 将 Fixture 定义在 conftest.py 中以便复用。
  2. 避免 Fixture 逻辑过于复杂,保持单一职责。
  3. 优先使用 yield 而非 addfinalizer(代码更简洁)。
  4. 用 scope 减少重复初始化 (如数据库连接池用 session 作用域)。

通过灵活使用 Fixture,你可以实现:

  • 资源复用(减少重复代码)
  • 依赖管理(清晰表达测试需求)
  • 环境隔离(避免测试间干扰)

掌握后,测试代码会变得更简洁、可维护! 🚀

相关推荐
笨小孩@GF 知行合一39 分钟前
易语言-高级应用
数据库·编程·易语言·中文编程
KaiwuDB1 小时前
KaiwuDB 运维实战04:DRBD + KaiwuDB——物联网场景下的低成本数据库高可用方案
运维·数据库·物联网·时序数据库·kaiwudb·aiot·多模数据库
夜雪一千3 小时前
MySQL外键彻底入门:是什么、怎么用、什么时候不该用
数据库·mysql
qq_334466863 小时前
删除SSMS的历史登录记录
数据库·sql
zl_dfq3 小时前
Redis 之 【常见面试题总结】(从数据类型到高可用、缓存一致性全解析)
数据库·redis
龙亘川3 小时前
铭记英烈守初心,数字强基促服务:亘川智城退役军人服务系统的实践与价值
java·数据库·人工智能
kv1104 小时前
Android studio国内开发有关
java·数据库·android studio
哭泣方源炼蛊5 小时前
正则表达式c++内容汇集
数据库·c++·mysql·正则表达式
余槐i5 小时前
EXPLAIN 显示走了索引查询仍慢:回表与选择性在 500 万行表上拉开 10 倍耗时
数据库·postgresql·性能优化·vacuum·explain