pytest 启动慢主因是 python 3.12 的 import 机制升级与插件链初始化方式叠加所致;禁用非必要插件、拆分 conftest.py、启用 --import-mode=importlib 可显著提速。

Pytest 启动慢、import 卡顿,90% 情况不是测试本身的问题,而是 Python 3.12 的 import 机制变化 + pytest 插件链初始化方式共同导致的。直接改 pyproject.toml 和禁用部分插件就能立竿见影。
为什么 Python 3.12 下 pytest 启动变慢了
Python 3.12 把 import 的模块生命周期控制从“隐式行为”升级为“显式契约”,解释器会在首次导入时做更多静态验证(比如 __spec__ 完整性、__loader__ 兼容性),尤其当项目里有大量 conftest.py、动态插件或非标准包结构时,这个开销会被放大。你看到的“卡在 import 阶段”或“pytest --help 响应迟缓”,往往就卡在这一步。
常见现象包括:
-
pytest --collect-only耗时超过 3 秒(小项目) - CI 中首次运行 pytest 时 CPU 占用突增但无输出,持续 5–10 秒
- 启用
pytest-xdist后 worker 进程启动延迟明显,--numprocesses=auto反而更慢
禁用非必要插件可减少 40%+ 初始化时间
pytest 默认会扫描所有已安装插件并调用其 pytest_configure 钩子,哪怕你没在命令行里用到它们。Python 3.12 对钩子函数签名和返回值的校验更严格,导致每个插件加载都多一次类型检查。
实操建议:
- 运行
pytest --trace-config查看实际加载了哪些插件,重点关注未被使用的第三方插件(如pytest-asyncio、pytest-litestar、pytest-benchmark) - 在
pyproject.toml中显式禁用:[tool.pytest.ini_options] addopts = [ "--disable-warnings", "-p", "no:asyncio", "-p", "no:litestar", "-p", "no:benchmark" ]
- 不要依赖
pip uninstall来“清理”——有些插件是其他依赖间接带入的,-p no:xxx是唯一可靠方式
优化 conftest.py 的 import 顺序与粒度
conftest.py 是 pytest 的“全局上下文”,但它不是 lazy loaded。Python 3.12 会强制解析其中所有顶层 import,哪怕某个 fixture 根本没被当前测试用到。
容易踩的坑:
- 在
conftest.py顶部import pandas as pd或from sqlalchemy import create_engine—— 这些重型库会拖慢所有测试启动 - 把数据库 fixture 和 API mock fixture 放在同一个
conftest.py里,导致单元测试也得加载 DB 相关模块
正确做法:
- 按 scope 和用途拆分
conftest.py:根目录放通用 fixture(如tmp_path),tests/integration/下放 DB 相关 fixture,tests/unit/下不放任何外部依赖 - 把重型 import 移到 fixture 函数体内(即 lazy import):
@pytest.fixture def db_connection(): from sqlalchemy import create_engine # ← 这里才 import return create_engine("sqlite:///:memory:") - 避免在
conftest.py里写if TYPE_CHECKING:块以外的类型导入(如from typing import TYPE_CHECKING, Any是安全的;from myapp.models import User不是)
启用 Python 3.12 的 import 缓存加速选项
Python 3.12 新增了 -X importtime 和更细粒度的缓存控制,但对 pytest 更实用的是 PYTHONMALLOC=malloc 和 importlib.metadata 的预热。
推荐配置(加到 CI 或本地开发环境):
- 设置环境变量:
PYTHONMALLOC=malloc(避免默认的 debug malloc 在 3.12 下额外开销) - 在
pyproject.toml的 pytest 配置中加入:[tool.pytest.ini_options] addopts = ["--import-mode=importlib"]
—— 强制使用 importlib 导入模式,绕过旧式__import__兼容路径 - 如果项目用 Poetry,确保
poetry.lock中importlib-metadata版本 ≥ 7.0(3.12 原生支持已移除该包,但某些插件仍依赖它)
最常被忽略的一点:Python 3.12 的 import 缓存不会跨进程共享。这意味着 pytest-xdist 的每个 worker 都要重走一遍 import 流程。如果你发现并行后总耗时没降多少,大概率是 conftest.py 或插件触发了重复重型 import —— 此时拆分 conftest.py 比调大 -n 数值更有效。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











