pytest运行时找不到模块的根本原因是启动方式错误:它将测试文件视为独立脚本执行,__package__为none,导致相对导入失败;正确做法是用pytest tests/从项目根目录启动,并配置pyproject.toml中pythonpath = ["src"]。

PyTest运行时找不到模块,根本不是路径问题而是启动方式错了
PyTest默认把测试文件当作独立脚本执行(__name__ == '__main__'),此时__package__为空,所有相对导入(如from ..utils import helper)必然失败。这不是PYTHONPATH没设对,是PyTest压根没按包的方式加载你的代码。
验证方法:在测试文件开头加一行print(__name__, __package__),运行后看到__package__为None就确认了。
- 不要双击或用
python test_xxx.py直接运行测试文件 - 必须用
pytest tests/或pytest myproject/tests/这种**从项目根目录出发、指定目录/模块名**的方式启动 - 确保项目根目录下有
pyproject.toml或setup.py(哪怕空的),让PyTest识别为可导入包
conftest.py里sys.path.append()只在当前会话生效,且极易污染其他测试
很多人在conftest.py里写sys.path.append('../src'),以为一劳永逸。实际这会导致:同一测试套件中不同测试文件看到的sys.path不一致;CI环境因路径差异直接挂掉;PyTest插件(如pytest-cov)统计路径错乱。
更稳妥的做法是统一用pyproject.toml配置:
[tool.pytest.ini_options] pythonpath = ["src"]
这个配置会被PyTest自动读取并注入sys.path,且只作用于当前测试会话,不干扰其他Python进程。
- 路径填相对项目根目录的路径,不是相对于
conftest.py - 避免用
os.getcwd()拼接路径——CI里工作目录不可控 - 如果src下有
__init__.py,优先考虑用pip install -e .安装为可编辑包
PYTHONPATH临时生效但跨平台行为不一致,Windows和Linux大小写敏感性不同
在Linux/macOS终端执行export PYTHONPATH=$PYTHONPATH:/abs/path/to/src,再跑pytest确实能绕过导入错误。但Windows CMD里set PYTHONPATH=...不支持$变量展开;PowerShell语法又不同;而且Windows文件系统默认不区分大小写,而Linux严格区分——你本地能跑通的import MyModule,放到CI的Ubuntu上可能报ModuleNotFoundError因为实际文件名是mymodule.py。
- 开发机和CI用同一套路径配置,别依赖环境变量“临时救急”
- 用
python -c "import sys; print([p for p in sys.path if 'src' in p])"确认路径是否真被加载 -
PYTHONPATH中的路径必须是绝对路径,相对路径会被忽略
最常被忽略的点:PyTest默认不扫描子目录下的conftest.py
如果你把conftest.py放在tests/unit/下,而运行的是pytest tests/,PyTest只会加载tests/conftest.py,不会递归找tests/unit/conftest.py里的sys.path修改。这意味着你在unit/里写的路径补丁,对tests/integration/里的测试完全无效。
解决办法只有两个:
- 所有
conftest.py都放在项目根目录或tests/顶层目录 - 改用
pytest --import-mode=importlib参数,强制PyTest用importlib机制加载模块(需PyTest ≥ 6.2)
后者更干净,因为它绕过了sys.path魔改,直接让每个测试模块按真实包结构解析导入路径——这才是Python本来该有的样子。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











