pytest执行1800个测试耗时超10分钟,builtins.compile占比高原因排查
排查方向与解决方法
1. pytest 配置不当导致的频繁compile
- 检查测试扫描范围配置
没有明确限制测试扫描路径时,pytest会递归遍历当前目录下所有可识别的Python文件,虚拟环境、临时脚本目录、静态资源目录下的py文件都会被编译,直接推高耗时。
你可以在pytest.ini/pyproject.toml中添加配置限制扫描范围:[pytest] testpaths = tests # 仅扫描tests目录下的测试文件 norecursedirs = venv .git __pycache__ dist build # 排除不需要扫描的目录 - 检查字节码缓存是否被禁用
如果环境变量设置了PYTHONDONTWRITEBYTECODE=1,或者运行pytest时添加了--no-cache-dir参数,会导致每次运行都要重新编译所有py文件,无法复用之前生成的.pyc字节码缓存。去掉上述配置后再运行测试,验证耗时是否下降。 - 检查断言重写相关配置
pytest默认的断言重写逻辑会调用builtins.compile处理测试文件的断言逻辑,如果你的测试文件里有大量动态生成的断言语句,或者配置了自定义断言处理插件,也可能触发额外的compile调用。你可以临时加--assert=plain参数运行测试,验证耗时是否有明显下降,如果下降明显再针对性排查断言相关插件的问题。
2. 业务代码/依赖库的compile调用
- 定位调用源
运行以下命令生成火焰图,直接定位builtins.compile的调用栈:
从火焰图中可以直接看到是pytest内部,还是第三方依赖、业务代码在调用compile。py-spy record -o pytest_profile.svg -- pytest <你的测试运行参数> - 常见高耗时场景排查
- 大量动态代码生成逻辑:比如用
exec/eval动态生成测试用例、动态生成ORM模型、动态渲染模板(如Jinja2每次运行都重新编译模板字符串),都会触发大量compile调用。 - 参数化测试使用不当:如果用循环+
exec的方式动态生成测试函数,替代原生的@pytest.mark.parametrize,每次运行都会重复编译动态生成的代码,耗时会大幅提升。 - 未缓存的正则编译:虽然你已经优化了部分正则编译调用,还要检查是否有运行时循环编译正则的逻辑,没有复用编译后的正则对象。
- 大量动态代码生成逻辑:比如用
3. 快速定位问题阶段
运行pytest --collect-only仅执行测试收集逻辑,不运行实际测试用例:
- 如果该步骤耗时已经接近9分钟,问题完全出在测试发现/编译阶段,优先排查第一类配置问题
- 如果该步骤耗时很短,大部分耗时出现在测试执行阶段,优先排查第二类依赖/业务代码问题
内容的提问来源于stack exchange,提问作者Deepak Selvakumar
相关产品推荐
相关产品推荐

