为什么在Windows上使用pytest无法在路径中显示当前目录?

Way*_*ner 5 python pytest python-3.4

我有以下文件夹结构;

myapp\
  myapp\
     __init__.py
  tests\
     test_ecprime.py
Run Code Online (Sandbox Code Playgroud)

我的密码是

C:\Users\wwerner\programming\myapp\
Run Code Online (Sandbox Code Playgroud)

我有以下测试设置:

import pytest
import sys
import pprint

def test_cool():
    pprint.pprint(sys.path)
    assert False
Run Code Online (Sandbox Code Playgroud)

产生以下路径:

['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']
Run Code Online (Sandbox Code Playgroud)

当我尝试时,import myapp出现以下错误:

ImportError: No module named 'ecprime'
Run Code Online (Sandbox Code Playgroud)

因此,似乎没有将当前目录添加到我的路径中。

通过将导入行更改为如下所示:

import sys
sys.path.insert(0, '.')
import myapp
Run Code Online (Sandbox Code Playgroud)

这样我就可以毫无问题地导入myapp了。

为什么在运行pytest时我的当前目录没有显示在路径中?是我唯一的解决方法,以插入.sys.path?(如果有问题,我正在使用Python 3.4)

Way*_*ner 6

啊啊!

比较我的cookiecutter回购的布局之后,事实证明比这更简单(更好)。

tests/
    __init__.py
    test_myapp.py
Run Code Online (Sandbox Code Playgroud)

__init__.py只需将文件添加到测试目录中,即可py.test从主目录运行。

  • pytest文档的“良好做法”部分明确建议不要这样做。这只会使您的包裹意外导入。如果您这样做,您将不得不使用基于src的项目布局。有关原因的所有详细信息,请参见https://github.com/pytest-dev/pytest/pull/2297/。 (2认同)
  • 哪种解决方案更糟糕?添加`_init__.py`?或者修改测试源中的sys.path? (2认同)

wim*_*wim 5

使用可安装包

如果您有可安装的包(setup.pypyproject.toml定义了构建系统的文件),那么最好针对已安装的代码进行测试。在venv中安装代码将使 import 语句在该 venv 中正确解析。

pip install --editable .
pytest
Run Code Online (Sandbox Code Playgroud)

将问题中显示的项目制作为可安装包的最简单的方法是添加以下内容setup.py

from setuptools import setup

setup(
    name="myapp",
    version="0.1",
    packages=["myapp"],
)
Run Code Online (Sandbox Code Playgroud)

这会将myapp代码放在虚拟环境的/path/to/myapp/.venv/lib/python3.XY/site-packages中。sys.path现在myapp可以从site-packages目录导入,就像用户安装一样。在测试执行期间当前工作目录既不必要也不可取。sys.path

不使用可安装包

问题中显示的项目没有任何安装程序,因此无法安装。仍然可以通过确保项目根目录(即包含myapptests作为子目录的目录)存在于sys.path.

执行此操作的最佳方法是使用python -m pytest,而不是调用裸pytest命令。使用时,python -m pytest它将当前工作目录添加到sys.path. 这是执行包时的正常 Python 行为__main__在此处记录),也是 pytest 的记录用法 - 请参阅调用pytestpython -m pytest

为什么添加__init__.pytests子目录有效(无效)?

问题中显示的目录结构是“在应用程序代码之外进行测试”模式,记录在此处。这也是我推荐的目录结构,因为它在库/应用程序代码和测试代码之间创建了明确的区别。

当使用“测试外部应用程序代码”结构时,不建议__init__.py在测试目录中添加文件,因为测试文件并不打算“打包”(例如,测试文件实际上不需要从其他测试文件导入,并且软件包的最终用户根本不需要安装它们)。

myapp/__init__.py添加实际上允许myapp导入的原因(如韦恩的答案pytest所示)实际上是一个意外,因为测试发现在测试收集阶段附加的方式。这在文档中被描述为“有问题”sys.path

...这引入了一个微妙的问题:为了从目录加载测试模块tests, pytest 将存储库的根添加到,这增加了现在也可以导入的sys.path副作用mypkg

如果您打算使用src-layout,他们继续强烈建议您使用__init__.py,以避免导入系统的这种混乱。

但也许不依赖这种副作用的最好理由是 pytest 集合实际上可以在多种模式下工作(请参阅导入模式),并且 Wayne 的答案依赖于 pytest 使用“前置”导入模式。Prepend 模式目前是默认模式,但文档提到未来版本将默认切换为使用“importlib”模式:

我们打算在未来的版本中设置importlib默认值。

接受的答案不起作用pytest --import-mode=importlib,因此在某个阶段将完全停止工作。