conftest.py 是 pytest 的 fixture 和钩子注入点,非配置文件;其作用域按测试文件路径向上逐级扫描,不跨兄弟目录;同名 fixture 会静默覆盖,命名需具业务语义;autouse=true 和钩子函数需谨慎使用;工具函数应移出 conftest.py。

conftest.py 不是“配置文件”,它是 pytest 的 fixture 和钩子注入点;用错层级或命名冲突,测试行为会静默改变,且极难排查。
conftest.py 的作用域规则必须手写验证,不能靠猜测
pytest 查找 conftest.py 是从测试文件所在目录**向上逐级扫描**,直到项目根目录,但**不会跨兄弟目录**。比如:
- 测试文件
tests/api/v1/test_login.py会依次加载:tests/api/v1/conftest.py→tests/api/conftest.py→tests/conftest.py→project_root/conftest.py(如果存在且在测试路径内) - 但
tests/api/v1/conftest.py**完全看不到**tests/ui/conftest.py里的任何东西 - 同名
@pytest.fixture在子目录中会直接覆盖父目录的,且不报错
常见错误现象:你在 tests/conftest.py 定义了 @pytest.fixture(scope="session") def db_connection(),又在 tests/integration/conftest.py 里重写了同名 fixture,结果所有 integration 测试都用了新版本,而 unit 测试仍用旧版——表面一切正常,实则数据隔离被破坏。
fixture 命名必须带业务语义,避免 generic 名称
用 sample_data、obj、fixture1 这类名字,会在多层 conftest.py 中迅速失控。pytest 不校验 fixture 名称是否唯一或是否有歧义,只按作用域就近绑定。
- ✅ 推荐:
@pytest.fixture def valid_user_token()、@pytest.fixture def expired_jwt_header() - ❌ 危险:
@pytest.fixture def token()—— 很可能被其他模块的tokenfixture 覆盖,且你无法一眼看出它代表什么 - autouse=True 更要谨慎:全局
@pytest.fixture(autouse=True)会影响所有测试,包括你临时加的print()调试用例
钩子函数必须显式声明 @pytest.mark.optionalhook
不是所有钩子都能直接写进 conftest.py 就生效。比如定制 HTML 报告时常用的几个 hook:
-
pytest_html_results_table_header和pytest_html_results_table_row必须加@pytest.mark.optionalhook,否则静默忽略 -
pytest_configure和pytest_runtest_makereport是自动识别的,无需额外标记 - 拼错钩子名(如写成
pytest_html_result_table_header)也不会报错,只是对应功能不生效
示例(修正 report 描述列):
@pytest.mark.optionalhook
def pytest_html_results_table_header(cells):
cells.insert(1, html.th("Description"))
cells.pop(-1) # 删除 Link 列
<p>@pytest.mark.optionalhook
def pytest_html_results_table_row(report, cells):
cells.insert(1, html.td(report.description))
cells.pop(-1)</p><p>@pytest.hookimpl(tryfirst=True, hookwrapper=True)
def pytest_runtest_makereport(item, call):
outcome = yield
report = outcome.get_result()
report.description = str(getattr(item.function, "<strong>doc</strong>", ""))
</p>
不要把 conftest.py 当成“万能工具箱”
有人习惯在 conftest.py 里塞工具函数、常量、甚至 mock 辅助类。这会导致:
- 测试文件导入变慢(每次收集测试都要执行整个
conftest.py) - 循环依赖风险升高(比如工具函数 import 了 src 某模块,而该模块测试又依赖这个 fixture)
- IDE 自动补全混乱,跳转失效
正确做法:
- fixture 只做资源准备/销毁(数据库连接、临时目录、mock 对象实例)
- 工具函数放进
tests/utils/或tests/lib/,再由测试文件显式 import - 共享常量统一放在
tests/conftest.py顶层(非 fixture 函数内),并用大写命名(如TEST_TIMEOUT = 5)
最易被忽略的一点:当你在多个 conftest.py 中定义了不同 scope 的同名 fixture(比如一个 scope="function",一个 scope="module"),pytest 不会警告,而是按作用域规则选择其一——你得自己查文档确认哪个 scope 生效,而不是凭感觉猜。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











