如何在Python包目录内运行测试并解决相对导入报错?
你的Python包目录结构如下:
my_package/ __init__.py my_module.py my_helper/ __init__.py work.py tests/ __init__.py test_work.py
- 在
my_package目录内执行python -m unittest时,会触发ValueError: attempted relative import beyond top-level package错误——原因是test_work.py中使用了from ..my_helper import work的相对导入,此时__package__为tests,__name__为tests.test_work,Python会把tests识别为顶层包,导致相对导入越界。 - 回到
my_package的父目录执行python -m unittest则测试正常,此时__package__为my_package.tests,__name__为my_package.tests.test_work,相对导入的层级关系正确。
你希望无需切换目录,直接在my_package内运行python -m unittest、nosetests或pytest,让测试以正确的包层级运行。
方案1:改用绝对导入(推荐长期方案)
把测试文件中的相对导入替换为基于顶层包my_package的绝对导入:
# test_work.py from my_package.my_helper import work
这种方式不依赖运行目录或环境变量,只要my_package在Python的模块搜索路径(sys.path)中,就能稳定运行测试。
如果不想修改大量导入代码,可以在my_package/tests/__init__.py中添加代码,将my_package的父目录加入搜索路径:
# my_package/tests/__init__.py import sys from pathlib import Path # 将my_package的父目录加入sys.path sys.path.append(str(Path(__file__).parent.parent.parent))
这样在my_package目录内运行测试时,Python能识别到顶层的my_package包,相对导入就能正常解析。
方案2:用pytest的--rootdir参数(针对pytest)
如果使用pytest,直接在my_package目录内执行:
python -m pytest --rootdir ..
--rootdir指定项目根目录(即my_package的父目录),pytest会自动把根目录加入模块搜索路径,测试中的相对导入层级会被正确识别。
方案3:临时设置PYTHONPATH环境变量
在my_package目录内,先设置环境变量再运行测试:
Linux/macOS
export PYTHONPATH=$(pwd)/.. && python -m unittest # 运行nosetests同理 export PYTHONPATH=$(pwd)/.. && nosetests
Windows(PowerShell)
$env:PYTHONPATH = "$(Get-Location)\.." ; python -m unittest
该操作会把my_package的父目录临时加入Python模块搜索路径,让Python识别my_package为顶层包,解决相对导入越界问题。
不需要必须从父目录运行测试,通过上述方法即可在my_package目录内直接执行测试。其中改用绝对导入是最稳妥的长期方案,避免依赖环境变量或工具参数;如果不想修改代码,添加sys.path配置、设置PYTHONPATH或使用pytest的--rootdir参数都是可行的快捷方案。
内容的提问来源于stack exchange,提问作者tread

