# pytest 的 5 个 fixture 骚操作,我用了 3 年才学会

一句话结论:yield 做清理、fixture 嵌套 3 层、params 一行生成测试矩阵、scope 控制初始化频率、conftest.py 自动共享------5 个操作让你的测试代码少写一半。


📊 先看对比

维度 只会 return 掌握 5 个操作后
setup/teardown 两个函数分开写 一个 yield 搞定
多层依赖 全揉在一个 fixture 里 各管一层,清晰可复用
多场景测试 手写 N 个测试函数 一行 params 自动生成
初始化次数 每个测试都重新初始化 scope 精确控制
fixture 共享 每个文件手动 import conftest.py 自动发现

一、只会 return 的日子

三年前我写 pytest,fixture 只有一个用法:

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

直到有一天------3000 条测试数据残留,因为我不知道怎么写 teardown。

pytest 的 fixture 比 unittest 的 setup/teardown 强十倍。 花了三年,我把玩法升级到了 5 个等级。


二、骚操作 ①:yield ------ setup + teardown 一锅端

不用把 setup 和 teardown 写两个函数。yield 前面的代码是 setup,后面的代码是 teardown:

python 复制代码
@pytest.fixture
def test_user(db_session):
    # == setup ==
    user = User(name="张三")
    db_session.add(user)
    db_session.commit()

    yield user  # 交给测试函数

    # == teardown(自动执行)==
    db_session.delete(user)
    db_session.commit()

加 try/finally 保证一定清理:

python 复制代码
@pytest.fixture
def temp_file():
    path = "/tmp/test.json"
    try:
        yield path
    finally:
        os.remove(path)  # 测试失败也会执行

三、骚操作 ②:嵌套 ------ fixture 调 fixture

一个测试要 db → user → order,不用全揉在一起:

python 复制代码
@pytest.fixture
def db():
    conn = create_db()
    yield conn
    conn.close()

@pytest.fixture
def user(db):         # 依赖 db
    u = User(name="张三")
    db.add(u); db.commit()
    yield u
    db.delete(u); db.commit()

@pytest.fixture
def order(db, user):  # 依赖 db + user
    o = Order(user_id=user.id, amount=99)
    db.add(o); db.commit()
    yield o
    db.delete(o); db.commit()

测试函数:

python 复制代码
def test_cancel(order):  # 只声明最上层,pytest 自动注入依赖链
    order.cancel()
    assert order.status == "cancelled"

3 层依赖,改哪层动哪层,互不干扰。

⚠️ 踩坑:别写循环依赖。a(b) 和 b(a) 同时存在 → pytest 直接报错。


四、骚操作 ③:参数化 ------ 一行代码生成测试矩阵

3 种支付 × 2 种用户 = 6 个用例?不用手写 6 个函数:

python 复制代码
@pytest.fixture(params=["wechat", "alipay", "bank_card"])
def payment_method(request):
    return request.param

@pytest.fixture(params=["vip", "normal"])
def user_type(request):
    return request.param

def test_payment(payment_method, user_type):
    """自动生成 3×2=6 个测试"""
    result = pay(method=payment_method, user=user_type)
    assert result.success

输出:

css 复制代码
test_payment[wechat-vip]      PASSED
test_payment[wechat-normal]   PASSED
test_payment[alipay-vip]      PASSED
test_payment[alipay-normal]   PASSED
test_payment[bank_card-vip]   PASSED
test_payment[bank_card-normal] PASSED

加上 ids 自定义中文名:

python 复制代码
@pytest.fixture(params=[
    ("wechat", 100),
    ("alipay", 0.01),
], ids=["微信-正常", "支付宝-最小"])

五、骚操作 ④:scope ------ 控制初始化频率

db 每个测试都重建 → 300 个测试建 300 次连接。加 scope:

python 复制代码
@pytest.fixture(scope="session")   # 整个测试会话只执行一次
def db():
    conn = create_db()
    yield conn
    conn.close()

@pytest.fixture(scope="module")    # 每个测试文件执行一次
def api_client(app_config):
    return APIClient(app_config)

@pytest.fixture                    # 默认 function,每个测试执行
def fresh_order(db):
    order = Order()
    yield order
    db.delete(order)

四种 scope:

scope 时机 适用
function 每个测试 大部分 fixture
class 每个测试类 类内共享
module 每个 .py 文件 模块级配置
session 整个测试会话 数据库连接

⚠️ 踩坑:session 级 fixture 不能依赖 function 级 fixture,会报 ScopeMismatch。


六、骚操作 ⑤:conftest.py ------ 不 import,自动共享

fixture 放在 conftest.py 里,pytest 自动发现,不需要 import:

复制代码
tests/
├── conftest.py          ← 全局 fixture
├── unit/
│   ├── conftest.py      ← 只对 unit/ 生效
│   └── test_user.py
└── integration/
    ├── conftest.py      ← 只对 integration/ 生效
    └── test_payment.py

tests/conftest.py:

python 复制代码
@pytest.fixture(scope="session")
def db():
    conn = create_db()
    yield conn
    conn.close()

tests/integration/test_payment.py:

python 复制代码
def test_pay(api_client):  # 不用 import!pytest 自动找到
    resp = api_client.post("/pay", {"amount": 100})
    assert resp.status_code == 200

子目录继承父目录的 conftest,反过来不行。


七、速查表

# 操作 一句话
① yield setup + teardown 写一起
② 嵌套 fixture 调 fixture,各管一层
③ 参数化 params 一行生成测试矩阵
④ scope 精确控制初始化频率
⑤ conftest 不 import,自动共享

💬 你的 fixture 现在用到第几个骚操作了?评论区报个数。

📌 下一篇预告:《GitHub Actions 自动化测试→部署一条龙》--- push 代码,全自动跑。

🛰️ 公众号:测开实战派 | 专注测试开发 × DevOps 实战分享


🏷️ 标签:Python · pytest · 测试 · 自动化测试

2026 年 6 月 | 约 2500 字 | 预计阅读 6 分钟

相关推荐
Lyy8 小时前
DevOps平台 — 第十三篇:规划的设计与实现
后端·devops
troy12810 小时前
从 JIRA 到监控:一条完整的 DevOps 交付流水线实战
运维·devops·jira
智能运维指南1 天前
软件测试管理系统选型:如何打通「需求—用例—缺陷」的质量闭环
devops·嘉为蓝鲸·软件测试管理系统
EdgeEcho2 天前
我的 GitHub Action 本地全绿,放上去五个 bug——包括一个让文件完全不执行的
devops
分布式存储与RustFS7 天前
MinIO 官方 Docker 镜像被移除:依赖它的项目该怎么办
docker·云原生·devops·对象存储·minio·分布式存储
あ-7 天前
python3 ModuleNotFoundError: No module named ‘_sqlite3‘
devops
研发小能7 天前
制造行业 DevOps 国产替代怎么落地?面向离散制造与汽车集团的横向比较
研发效能·devops·嘉为蓝鲸
Mr.朱鹏8 天前
Docker三剑客实战指南:Docker、Dockerfile 和 Docker Compose
python·docker·devops·dockerfile
Liaiyang668 天前
# 自研 AST 容错初筛工具:横向评测 Django、PyTorch、TensorFlow 三大开源 Python 框架异常收容风险
python·测试工具·自动化·开源软件·代码规范·devops·代码复审
あ-8 天前
基于TencentOS的PVMCubeSandBox模板构建失败
devops