# 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 分钟

相关推荐
爱学习的程序媛3 小时前
DevOps 深度解析:从文化理念到落地实践
运维·devops
至乐活着1 天前
Docker Compose多服务编排实战:从零搭建Node.js+MySQL+Redis全栈应用
docker·微服务·devops·容器编排·compose
热爱运维的小七1 天前
深度解析|应用性能 + RUM + 拨测:现代 IT 运维的可观测性“铁三角”
运维·it运维·devops·apm·rum·网站拨测
A.说学逗唱的Coke1 天前
【大模型专题】AIOps + Loop 工程:从智能告警到自愈闭环的实战指南
运维·人工智能·devops
平头老王1 天前
CI/CD流水线设计 — 第1章:常见误区
ci/cd·自动化·devops·持续部署·持续集成
wanghao6664552 天前
DevOps 从入门到实践:构建高效交付流水线
运维·devops
悠悠121382 天前
AWS DevOps Agent 体验一周后,我决定把 oncall 手机调成静音了
云计算·aws·devops
又是进步的一天2 天前
一台虚拟机学习CI流程
学习·ci/cd·云原生·容器·kubernetes·devops
逻极4 天前
Jenkins 从入门到精通:CI/CD自动化流水线实战
ci/cd·自动化·jenkins·devops