pytest 建议包含一个额外的目录来分隔项目中的源代码:
my_package
├── src # <-- no __init__.py on this layer
│ └── my_package
│ ├── __init__.py
│ └── util_module
│ ├── __init__.py
│ └── utils.py
└── tests
├── __init__.py
└── test_util_module
├── __init__.py
└── test_utils.py
可悲的是,他们没有提到[1]关于测试代码中的导入在这种情况下应该如何工作,这对于我的IDE在这个简单的示例[2]中工作得很好,但会导致以下错误: py测试:
~/my_package$ pytest
====================== test session starts ======================
platform linux -- Python 3.6.4, pytest-3.5.1, py-1.5.3, pluggy-0.6.0
rootdir: /home/user/workspace/my_package, inifile:
collected 0 items / 1 errors
============================ ERRORS =============================
___ ERROR collecting tests/test_util_module/test_utils.py ___
ImportError while importing test module '/home/user/workspace/my_package/tests/test_util_module/test_utils.py'.
Hint: make sure your test modules/packages have valid Python names.
Traceback:
tests/test_util_module/test_utils.py:1: in <module>
from test.test_module.some_file import starify
E ModuleNotFoundError: No module named 'my_package.util_module'
!!!! Interrupted: 1 errors during collection !!!!!
我可以通过将测试的导入更改为
来解决该问题from src.my_package.util_module.utils import starify
但是我的 IDE 抱怨
src
部分是多余的,所以我想将其保留。
[1]:不再是这样了。从版本 3.7.3 开始,pytest 建议使用可编辑安装,这也出现在 @hoefling 的答案中,位于其良好实践顶部。
[2]:设置为
virtualenv env -p python3.6; source env/bin/activate; pip install pytest
pytest>=7
的推荐方法:使用pythonpath
设置最近,
pytest
添加了一个新的核心插件,支持通过sys.path
配置值进行
pythonpath
修改。因此,现在解决方案更加简单,不再需要任何解决方法:
pyproject.toml
示例:
[tool.pytest.ini_options]
pythonpath = [
"src"
]
pytest.ini
示例:
[pytest]
pythonpath = src
路径条目是相对于根目录计算的,因此在这种情况下,
src
条目将path/to/project/src
目录添加到sys.path
。
还允许多个路径条目:对于布局
repo/
├── src/
| └── lib.py
├── src2/
| └── lib2.py
└── tests
└── test_lib.py
配置
[tool.pytest.ini_options]
pythonpath = [
"src", "src2",
]
或
[pytest]
pythonpath = src src2
会将
lib
和 lib2
模块添加到 sys.path
,所以
import lib
import lib2
两者都可以工作。
调整
PYTHONPATH
(如评论中的建议)是解决导入问题的一种可能性。另一种是在 conftest.py
目录中添加一个空的 src
文件:
$ touch src/conftest.py
和
pytest
会将 src
添加到 sys.path
。这是欺骗 pytest
将代码库添加到 sys.path
的简单方法。
但是,当您打算构建发行版时,通常会选择
src
布局,例如提供 setup.py
(在本例中)显式指定根包目录:
from setuptools import find_packages, setup
setup(
...
package_dir={'': 'src'},
packages=find_packages(where='src'),
...
)
并在开发过程中以开发模式安装包(通过
python setup.py develop
或 pip install --editable .
)。这样,您的包my_package
就可以正确集成到Python的站点包结构中,无需摆弄PYTHONPATH
。
从 PyTest 7.0.0 开始,您现在可以使用
pythonpath
选项在 sys.path
中设置一些默认条目。这是最方便的方法。
在
pytest.ini
:
[pytest]
pythonpath = src/
在
pyproject.toml
:
[tool.pytest.ini_options]
pythonpath = "src/"
如果您使用
tox
,那么您应该在其设置中取消设置,这样您就不会意外测试本地版本,而不是已安装的版本。
在
tox.ini
:
[testenv]
commands = pytest ... -o pythonpath=
请参阅文档:https://docs.pytest.org/en/7.1.x/reference/reference.html#confval-pythonpath
使用 github 操作时,PYTHONPATH 更新对我不起作用(已知问题)。使用 pytest-pythonpath 安装和 pytest.ini 文件对我有用:
pip install pytest-pythonpath # accompany with python_path in pytest.ini, so PYTHONPATH is updated with location for modules under test
有了这个,基本的“pytest”命令很高兴地找到了子目录中的所有测试,并根据我的 pytest.ini 找到了正在测试的模块(设置为匹配 pycharm 中的源文件夹)
演出有点晚了,但我相信可以在
sys.path
中更新 tests/__init__.py
并且它应该可以工作。我相信这就是该插件的作用。
例如。就我而言,目录结构如下所示:
repo/
├── src/
| └── foo.py
└── tests
├── pytest.ini
├── conftest.py
├── __init__.py # update sys.path here
├── integration
│ └── ...
└── unit
├── __init__.py
└── test_foo.py
所以在
__init__.py
我只是这样做:
import sys
sys.path.append("src")
此后
pytest ./tests
就可以工作了。