i具有以下文件夹结构;
myapp
myapp
__init__.py
tests
test_myapp.py
我的PWD是
C:Userswwernerprogrammingmyapp
我有以下测试设置:
import sys
import pprint
def test_cool():
pprint.pprint(sys.path)
assert False
产生以下路径:
['C:\Users\wwerner\programming\myapp\tests',
'C:\Users\wwerner\programming\envs\myapp\Scripts',
'C:\Windows\system32\python34.zip',
'C:\Python34\DLLs',
'C:\Python34\lib',
'C:\Python34',
'C:\Users\wwerner\programming\envs\myapp',
'C:\Users\wwerner\programming\envs\myapp\lib\site-packages']
,当我尝试使用import myapp
时,我会收到以下错误:
ImportError: No module named 'myapp'
,看起来它没有将当前目录添加到我的路径中。
通过更改我的导入行以看起来这样:
import sys
sys.path.insert(0, '.')
import myapp
我可以毫无问题地导入myapp
。
为什么我的当前目录在运行pytest时不会出现在路径中?我唯一的解决方法是将.
插入sys.path
?(如果很重要,我正在使用Python 3.4)
啊!
比较了我的CookieCutter回购的布局后,事实证明它比这更简单(和更好)。
tests/
__init__.py
test_myapp.py
简单地将__init__.py
文件添加到我的测试dir中,我可以从我的主目录运行py.test
。
使用可安装软件包
如果您有一个可安装的软件包(已定义的构建系统定义的setup.py
或pyproject.toml
文件),则最好针对安装的代码进行测试。在VENV中安装代码将使导入语句正确解决该VENV。
pip install --editable .
pytest
将问题中显示为可安装软件包中显示的项目的最简单方法是添加此setup.py
:
from setuptools import setup
setup(
name="myapp",
version="0.1",
packages=["myapp"],
)
这将把myapp
代码放在虚拟环境的sys.path
中的/path/to/myapp/.venv/lib/python3.XY/site-packages
上。现在可以从site-packages
DIR导入myapp
,就像用户安装一样。在测试执行过程中,当前的工作目录既不需要也不希望在sys.path
上存在。
不使用可安装的软件包
问题中显示的项目没有任何安装程序,因此无法安装。它仍然可以通过确保项目root(即包含myapp
和tests
作为子目录的目录)进行测试。
最好的方法是使用python -m pytest
,而不是调用Bare pytest
命令。当您使用python -m pytest
时,它将当前的工作目录添加到sys.path
的开始。当执行包裹为__main__
(在此处记录)时,这是正常的Python行为,并且也是Pytest的记录用法 - 请参见调用pytest
与python -m pytest
。
为什么将__init__.py
添加到tests
子目录(不工作)?
问题中显示的目录结构是" 测试"应用程序代码'图案,在这里记录。这也是我建议的目录结构,因为它在库/应用程序代码和测试代码之间创建了一个明确的区别。
使用" 测试"应用程序代码&quot时,不建议在测试目录中添加__init__.py
文件。结构,由于测试文件并非打算"打包"(例如,测试文件实际上不需要从其他测试文件中导入,并且无需安装包装的最终用户)。
添加myapp/__init__.py
的原因实际上允许myapp
由pytest
导入(如Wayne的答案所示)实际上是事故,因为在测试收集阶段测试发现附加sys.path
的方式。这被描述为"有问题"在文档中
...这引入了一个微妙的问题:为了加载来自
tests
目录的测试模块,PyTest将存储库的根本置于sys.path
,这增加了mypkg
现在也可以导入的副作用,该副作用也可以导入
,如果您打算在测试目录中包含__init__.py
文件,以避免导入系统的这种混乱。
但是,不依赖这种副作用的最佳理由也许是Pytest Collection实际上可以在多种模式下使用(请参阅导入模式),而Wayne的答案依赖于使用" Prepend"的Pytest。导入模式。预端模式当前是默认值,但文档提到的是将来版本将切换为使用" Ementlib"。默认模式:
我们打算使
importlib
未来版本中的默认值。
所接受的答案不适用于pytest --import-mode=importlib
,因此在某个阶段将完全停止工作。
sys.path自动包含脚本的目录,而不是当前的工作目录。
我猜想您的脚本放在tests
目录中。基于此假设,您的代码应该看起来像这样:
import sys
import os
ROOT_DIR = os.path.dirname(os.path.dirname(__file__))
sys.path.append(ROOT_DIR)
import myapp # Should work now
使用环境变量PYTHONPATH
。
Windows:
set PYTHONPATH=.
py.test
in unix:
PYTHONPATH=. py.test