本节摘要:fixture 是 pytest 给"测试前提"起的正式名字——带名字的原料供应,负责在每个用例前把现场搭好、用完把现场拆掉。本节讲透夹具的解剖结构:yield 分界装配与拆卸、scope 决定共享半径、conftest 负责跨文件共享,以及工厂函数与参数化在数据供给上的分工。读完你应能把上一节"独立"柱子的要求变成不假思索的机械动作。
小林在共享购物车上又翻了一次车:他给折扣测试建了个模块级 cart 变量,上午还绿着,中午加的新用例往同一辆车里塞了件商品,下午整批一跑,折扣测试全红。老周看完只说了一句:"前提要么自己造,要么交给夹具——反正不能躺在模块上等人来共享。"这句话里的"交给夹具",就是本节的主角。
上一节的"独立"柱子要求每条测试自带前提、用完自清。手工满足这个要求很快就会遇到麻烦:二十条测试都要"一辆装了三件商品的购物车",把构造代码复制二十遍是重复;抽成一个公共变量又回到小林的翻车现场——共享的是实例,不是构造方法。pytest 的 fixture 解决的正是这个矛盾:共享构造过程,不共享实例。每个用例声明自己需要什么原料,pytest 在用例开始前现场装配一份全新的,用完按需拆卸。
import pytest from decimal import Decimal from shop.cart import Cart @pytest.fixture def cart(): """全新的空车——每个用例各得一份,互不知道对方存在。""" return Cart() @pytest.fixture def cart_with_three_items(cart): """夹具可以消费夹具:装配过程同样能复用。""" cart.add(sku="BOOK", price=Decimal("30.00"), qty=1) cart.add(sku="PEN", price=Decimal("3.50"), qty=2) return cart def test_total_is_sum(cart_with_three_items): assert cart_with_three_items.total() == Decimal("37.00")
用例把夹具名写成参数,pytest 自动注入。依赖链也顺理成章:cart_with_three_items 声明要 cart,pytest 递归装配。测试意图反而更清楚了——用例签名直接回答"这条测试的前提是什么"。
拆卸需求在碰到文件、临时目录、数据库连接时立刻出现。pytest 的写法是在夹具里用 yield 分界:之前是装配,之后是拆卸,异常时拆卸照样执行。
@pytest.fixture def temp_ledger(tmp_path): ledger = Ledger(db_path=tmp_path / "ledger.sqlite") # 装配 ledger.connect() yield ledger # 用例在此运行 ledger.close() # 拆卸,异常也执行
顺带注意 tmp_path:pytest 内置的每用例临时目录,用完自动清理。内置夹具还有管理补丁的 monkeypatch(第 5 章治时间随机时重度使用)和捕获输出的 capsys——先混个脸熟,用得到时它们都在。

夹具解决"构造",数据本身还有一讲究。三条经验排个序:
**少量固定值优先。**测试数据要的是可解释,不是逼真。"BOOK" 比"诺贝尔文学奖得主观感"好——失败信息里看得懂。重复构造用工厂函数,默认值兜底、参数覆盖个别字段:
def make_item(**overrides): defaults = dict(sku="SKU-1", price=Decimal("10.00"), qty=1) defaults.update(overrides) return Item(**defaults) def test_zero_price_item_is_rejected(): cart = Cart() cart.add(make_item(price=Decimal("0"))) assert cart.total() == Decimal("0")
同输入不同期望交给参数化(2.4 的规矩),工厂与参数化是互补而非竞争。至于随机数据——测试要确定性,随机种子必须固定或干脆别用;想让"奇怪输入"参与测试,交给属性测试工具或显式边界清单,不交给掷骰子。
夹具与参数化还能组合出"装配矩阵":同一套前提,按参数换不同装配结果,一次声明覆盖多种现场。常见于"同一规则在不同会员等级下"这类场景——夹具管装配的形状,参数管场景的枚举,用例本体保持一条。组合的度在于可读性:一旦夹具签名膨胀到看不懂,说明装配复杂度该退回工厂函数里去,别硬塞在夹具参数上。
⚠️ 夹具最常见的滥用是把"步骤"当"原料":一个夹具里连做注册、下单、支付三件事,用例根本看不出自己在哪一步开始。原料要细粒度,剧情留给用例自己演。
💡 判断夹具设计好坏的一条速测:把任意一条用例单独拎出来跑,它需要的全部上下文都应从夹具签名读出来。签名读不全的,就是有隐藏前提。
最后一条守则呼应本章的立场:夹具技术不难,难的是把"测试现场自管"当成默认审美。当团队里新写的测试自动长成"夹具供给、用例干净"的形状时,3.1 那四根柱子就不再需要逐条检查——它们已经长在土里了。现场管好了,还差最后一块骨架:外部依赖怎么挡在门外——下一节替身登场。