fixture是pytest的声明式依赖注入机制,通过函数参数名匹配自动注入资源,依赖scope控制生命周期,支持fixture间链式依赖且禁止循环引用,必须定义在conftest.py中实现共享。

fixture 是 pytest 的依赖注入机制,不是装饰器或类继承
很多人误以为 @pytest.fixture 是个“高级装饰器”,其实它是 pytest 内置的**声明式依赖声明工具**。函数参数名匹配 fixture 名,pytest 就自动注入——不靠反射、不靠全局注册,只靠名字绑定和作用域调度。
关键点在于:fixture 本身是普通函数,返回值就是被注入的对象;调用时机由测试函数的参数列表决定,而非手动 call() 或 yield 控制(除非你主动写 yield)。
- fixture 函数不能带非默认参数(否则 pytest 启动就报
fixture 'xxx' has unused arguments) - 如果测试函数参数叫
db,就必须存在名为db的 fixture,大小写、下划线都必须完全一致 - 多个测试函数共用同名 fixture,pytest 默认只执行一次(取决于
scope),不是每次调用都重建
scope 参数决定依赖生命周期,不是“要不要注入”而是“注入几次”
scope 控制 fixture 的复用粒度,直接影响状态共享和资源开销。常见取值:"function"(默认)、"class"、"module"、"session"。选错会导致测试污染或资源泄漏。
比如数据库连接:
@pytest.fixture(scope="module")
def db():
conn = sqlite3.connect(":memory:")
yield conn
conn.close()
- 用
scope="function":每个测试都新建+关闭连接,安全但慢 - 用
scope="module":整个 test_*.py 文件只连一次,需确保测试不改 schema 或 commit 数据 - 用
scope="session":跨文件共享,一旦某个测试执行了conn.execute("DROP TABLE ..."),后续所有测试都会失败
fixture 之间可以互相依赖,但禁止循环引用
一个 fixture 可以把另一个 fixture 当作参数传入,pytest 会按依赖拓扑自动排序执行顺序。这比手写 setup/teardown 更清晰,也天然支持组合。
例如:
@pytest.fixture
def user(db):
return User.create(db, name="alice")
@pytest.fixture
def client(user):
return TestClient(app)
-
client依赖user,user又依赖db,pytest 自动先跑db→ 再user→ 最后client - 如果写成
def user(user):或def db(client):,pytest 启动时直接报错:fixture 'xxx' is involved in a cyclic dependency - 依赖链过长(>4 层)容易让调试变困难——出错时堆栈里看不到实际出问题的 fixture 函数名,只显示 pytest 内部调用
conftest.py 是共享 fixture 的唯一合理位置
不要在测试文件里重复定义相同 fixture;也不要把 fixture 放进 __init__.py 或随便一个 utils.py 里——pytest 只扫描 conftest.py(及其父目录的)来收集 fixture。
- 项目根目录放一个
conftest.py:适合全局 fixture,如tmpdir、caplog等扩展 - tests/integration/conftest.py:只对 integration 目录下的测试生效,可定义
live_api_client - pytest 会从测试文件所在路径向上查找最近的
conftest.py,就近原则覆盖,不是合并
最常被忽略的是:子目录的 conftest.py 中定义的 fixture,不会自动被父目录测试看到;反之,父目录的 fixture 默认对所有子目录可见——这点和 Python import 规则相反。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











