pytest的PATH问题“ ImportError:没有名为YadaYadaYada的模块”


231

我使用easy_install在Mac上安装pytest,并开始为文件结构像这样的项目编写测试:

repo/
repo/app.py
repo/settings.py
repo/models.py
repo/tests/
repo/tests/test_app.py

py.test在repo目录中运行时,所有行为均符合您的预期

但是,当我在linux或Windows上尝试相同的操作时(两者上都装有pytest 2.2.3),只要它从我的应用程序路径中首次导入某些内容,它就会发出吠声。举例来说from app import some_def_in_app

我是否需要编辑PATH才能在这些系统上运行py.test?有人经历过吗?


4
是使用setuptools修复它的方法。
ederag

4
请检查@hoefling答案,并考虑更改您接受的答案,如果SO允许了这么长时间:更好!
戴维(Davide)

Answers:


91

是的,如果您cd转到tests目录,则源文件夹不在Python的路径中。

您有2个选择:

  1. 手动将路径添加到测试文件,如下所示:

    import sys, os
    myPath = os.path.dirname(os.path.abspath(__file__))
    sys.path.insert(0, myPath + '/../')
  2. 使用env var运行测试PYTHONPATH=../


11
cd什么时候在目录上?我py.test从我的根开始。除非我弄错了,而且您的意思是pytest遍历我的文件夹
MattoTodd

如果这是一个cd问题,我也不会在Mac上打吗?
MattoTodd

哦,我误读了,并认为它在tests目录中不起作用。建议1中的技巧仍然有效。我仅使用Linux,所以无法解释其他操作系统上的行为。
Not_a_Golfer

您所有的test.py文件上都有类似的导入内容吗?
MattoTodd

4
是的,但是我的目录结构通常略有不同-我通常将/ src和/ test保留在根目录下。
Not_a_Golfer 2012年

275

我不确定为什么py.test不会在PYTHONPATH本身中添加当前目录,但这是一种解决方法(将从存储库的根目录执行):

python -m pytest tests/

之所以有效,是因为Python为您添加了当前目录到PYTHONPATH中。


2
如果您具有用于运行应用程序的代码,而不是在执行命令的级别上,则需要将相对导入重写为绝对导入。例如:project/test/all-my-tests并且project/src/app.py由于此更改,需要app.py使用__main__.pyin中的文件间接调用project/src,以便可以使用call python -m src。据我所知,这很混乱。
Zelphir Kaltstahl

3
@Zelphir:建议使用绝对导入。Habnabit的网站上有一篇关于包装最佳做法的好文章:blog.habnab.it/blog/2013/07/21/python-packages-and-you,而PEP8说:“切勿使用隐式相对导入,并且应在Python中将其删除。 3.” 请参阅:python.org/dev/peps/pep-0008
Apteryx

1
@Apteryx您的意思是“绝对项目”对吗?因为类似的事情/home/user/dev/projectxyz/src ...真的很糟糕,在大多数情况下不能在其他计算机上运行。我想我的意思是,即使模块与文件运行在同一文件夹中,我也必须始终将整个项目根目录写入模块路径。我不知道这是最佳做法,因此,这是有用的信息,谢谢。我仍然同意pep8的大部分内容,尽管它仍然不完美。
Zelphir Kaltstahl,

1
@Zelphir,是的,这就是我的意思。我相信Python中的绝对导入一词总是指“绝对项目”。请参阅:python.org/dev/peps/pep-0328/#rationale-for-absolute-imports。实际上,我敢肯定您至少不能使用默认的“导入”机制从随机绝对路径位置导入。
Apteryx

4
我添加__init__.py了测试,从而解决了问题。现在,我可以使用pytest
Kiran Kumar Kotari,

154

conftest

侵入性最低的解决方案是conftest.pyrepo/目录中添加一个空文件:

$ touch repo/conftest.py

而已。无需编写自定义代码来处理,sys.path也无需记住拖动PYTHONPATH或放入__init__.py不属于它的目录中。

之后的项目目录:

repo
├── conftest.py
├── app.py
├── settings.py
├── models.py
└── tests
     └── test_app.py

说明

pytestconftest在测试集合中寻找模块以收集自定义的钩子和固定装置,然后为了从中导入自定义的对象,请将pytest的父目录添加conftest.py到中sys.path(在本例中为repo目录)。

其他项目结构

如果你有其他的项目结构,将conftest.py包中的根目录(包含软件包,但不是包本身的人,所以并没有包含__init__.py),例如:

repo
├── conftest.py
├── spam
   ├── __init__.py
   ├── bacon.py
   └── egg.py
├── eggs
   ├── __init__.py
   └── sausage.py
└── tests
     ├── test_bacon.py
     └── test_egg.py

src 布局

尽管此方法可用于src布局(放置conftest.pysrc目录中):

repo
├── src
   ├── conftest.py
   ├── spam
      ├── __init__.py
      ├── bacon.py
      └── egg.py
   └── eggs 
       ├── __init__.py
       └── sausage.py
└── tests
     ├── test_bacon.py
     └── test_egg.py

请注意,添加srcPYTHONPATH减轻src布局的含义和好处!您将最终从存储库而不是已安装的软件包中测试代码。如果需要执行此操作,则可能根本不需要src目录。

从这往哪儿走

当然,conftest模块不仅仅是一些文件,可以帮助发现源代码。这是pytest框架的所有特定于项目的增强和测试套件的自定义的地方。pytestconftest整个文档中分散的模块上有很多信息; 开始于conftest.py:本地按目录插件

同样,SO在conftest模块上也有一个很好的问题:在py.test中,conftest.py文件的用途是什么?


2
@ aaa90210尽管我无法重现您的问题(从根目录中的conftest进行导入可以在任何级别上进行),但是您绝对不应从conftest文件中进行导入,因为它是conftest的保留名称,pytest强烈建议您不要这样做。这样,您就可以为将来的错误种植种子。创建另一个名为的模块utils.py,并将代码放置在其中以便在测试中重用。
霍夫林

4
竖起大拇指!这是唯一对我有效的解决方案。
130nrd

4
应该绝对是公认的答案-谢谢!
马丁·派克

2
从逻辑上讲,conftest.py它不属于应用程序代码,imo将其放在下面src/是不正确的。
Nik O'Lai

2
这个答案应该是SO上的固定标头。非常感谢。
Rigoberta Raviolini

119

我有同样的问题。我通过__init__.pytests目录中添加一个空文件来修复它。


77
请注意,py.test 建议这样做:avoid “__init__.py” files in your test directories. This way your tests can run easily against an installed version of mypkg, independently from the installed package if it contains the tests or not.SRC:pytest.org/latest/goodpractises.html
K.-Michael Aye

24
我来到这里时遇到了同样的问题,发现__init__.py从测试目录中删除可以为我解决。
101

3
@mafro我没看到问题吗?测试不必是可导入的代码,它们由测试运行程序发现。只有要测试的代码应该是已安装的软件包/模块,而不是测试。
K.-Michael Aye 2015年

5
__init__.py在的子目录中添加test/可以绝对导入,以针对要安装的模块在该子目录中运行特定测试。谢谢。
布莱斯·昆塔

7
你去了:doc.pytest.org/en/latest/goodpractices.html真的很容易在Google上找到。
K.-Michael Aye

46

pytest使用以下模块作为模块运行: python -m pytest tests


3
这似乎是一个可行的解决方案,但是有人可以解释为什么吗?我宁愿解决根本原因,也不要只是在python -m pytest没有其他解释的情况下使用,而是“因为它起作用”
Janne Enberg 18/08/23

4
例如,当项目层次结构为时,就会发生这种情况:package/src package/tests并且tests从中导入src。作为模块执行时,会将导入视为相对于执行位置而言是绝对的。
Stefano Messina

1
这个解决方案对我有帮助,谢谢!原因是由于Python版本中的冲突。pytest测试适用于早期的python版本。在我的情况下,我的python版本是3.7.1,python -m pytest测试有效,但pytest测试无效。
Ruxi Zhang

1
来自Pytest: “使用python -m pytest [...]而不是pytest运行pytest会产生几乎相同的行为,除了前一个调用会将当前目录添加到sys.path。”
Moad Ennagi '19

37

您可以在项目根目录中使用PYTHONPATH运行

PYTHONPATH=. py.test

或使用pip install作为可编辑导入

pip install -e .   # install package using setup.py in editable mode

3
这对我来说不起作用,因为test目录不在src目录结构中,并且从包含testsrc目录的目录中调用。
Zelphir Kaltstahl '16

21

我创建此文件是为了回答您的问题和我自己的困惑。希望对您有所帮助。注意py.test命令行和tox.ini中的PYTHONPATH。

https://github.com/jeffmacdonald/pytest_test

具体来说:您必须告诉py.test和tox在哪里可以找到要包含的模块。

使用py.test可以做到这一点:

PYTHONPATH=. py.test

并使用tox,将其添加到tox.ini中:

[testenv]
deps= -r{toxinidir}/requirements.txt
commands=py.test
setenv =
    PYTHONPATH = {toxinidir}

1
您能为您链接的项目做一个简短的解释吗?
JF Meier

1
也许只是我一个人,但是该项目上的自述文件非常详细,我对stackoverflow的评论说明了为什么创建回购协议。
杰夫·麦克唐纳

5
尽管这不是严格必要的,但通常的策略是将答案的主要内容包含在答案本身中,因为它可以确保从现在开始链接的资源可能已经消失了,从现在起的x年内,答案是可以理解的。
JF Meier

:) 那好吧。多数民众赞成在互联网上。
杰夫·麦克唐纳

9

我在Flask中遇到了同样的问题。

当我添加时:

__init__.py

到测试文件夹,问题消失了:)

应用程序可能无法将文件夹测试识别为模块


8

我通过删除__init__.py源文件的父文件夹中的顶层来修复它。


1
为我修复它。有人可以解释吗?
凌晨

这里的权利也为我解决了。绝对希望有人对此有一个解释
king_wayne

我添加了init .py,但仍然面临相同的问题,但是此解决方案也对我有用..原因是什么?
阿比吉特(Abhijit)

7

ConftestImportFailure: ImportError('No module named ...当我不小心将__init__.py文件添加到src目录中时,我开始出现奇怪的错误(这不应该是Python包,而只是所有来源的容器)。


3

由于出现了一些更简单的事情(您甚至可以说微不足道),我遇到了此错误。我没有安装pytest模块。因此apt install python-pytest,为我修复了一个简单的问题。

“ pytest”将在setup.py中列为测试依赖项。确保同时安装测试要求。


3

我有一个类似的问题。pytest无法识别我在工作环境中安装的模块。

我也通过安装pytest到同一环境中来解决它。


尽管我是从venv内部使用pytest的,但我也在全局安装了它,这给了我这个错误。卸载全局版本并在venv内安装后,它可以正常工作。
Markus Ressel

2

对我来说,这个问题是tests.py由Django和tests目录生成的。删除tests.py解决了问题。


2

我错误地使用了相对导入,因此出现了此错误。在OP示例中,test_app.py应该使用例如

from repo.app import *

但是,__ init__.py文件通常分散在文件结构中,除非文件和测试文件位于同一目录中,否则这将无法正常工作,并会产生类似ImportError的错误。

from app import *

这是我与一个项目有关的示例:

这是我的项目结构:

microbit/
microbit/activity_indicator/activity_indicator.py
microbit/tests/test_activity_indicator.py

为了能够从test_activity_indicator.py访问activity_indicator.py,我需要:

  • 使用正确的相对导入启动test_activity_indicatory.py:
    from microbit.activity_indicator.activity_indicator import *
  • 在整个项目结构中放置__init__.py文件:
    microbit/
    microbit/__init__.py
    microbit/activity_indicator/__init__.py
    microbit/activity_indicator/activity_indicator.py
    microbit/tests/__init__.py
    microbit/tests/test_activity_indicator.py

0

通常,由于无法导入模块而导致测试中断。经过研究,我发现系统在错误的位置查看文件,我们可以通过在其中复制包含模块的文件来轻松解决问题。与所述文件夹相同,以便正确导入。另一个解决方案建议是更改导入的声明,并向MutPy显示单元的正确路径。但是,由于多个单元可以具有此依赖性,这意味着我们还需要在其声明中提交更改,因此,我们宁愿将单元简单地移动到文件夹中。


还提供了OP的问题的其他答案,它们在一段时间前发布。发布答案时,请确保添加新的解决方案或实质上更好的解释,尤其是在回答较旧的问题时。有时最好在特定答案上发表评论。
help-info.de

由于除了从评论@ help-info.de:这里是链接到指南回答问题:stackoverflow.com/help/how-to-answer
NOD的手

0

根据Dirk Avery在Medium上发表的一篇文章(并得到我的个人经验的支持),如果您在项目中使用虚拟环境,则无法在系统范围内安装pytest;您必须将其安装在虚拟环境中并使用该安装。

特别是,如果在两个地方都安装了该pytest命令,则仅运行该命令将不起作用,因为它将使用系统安装程序。正如其他答案所描述的,一个简单的解决方案是运行python -m pytest而不是pytest; 之所以有效,是因为它使用了环境版本的pytest。或者,您可以只卸载系统的pytest版本。重新激活虚拟环境后,该pytest命令应起作用。


到目前为止,唯一对我有用的是python -m pytest tests/
Nik O'Lai

0

在遵循Flask教程时,我遇到了同样的问题,我在Pytest官方文档中找到了答案。 这与我(以及我认为还有许多其他人)用于做事的方式有些不同。

您必须setup.py至少使用以下两行在项目的根目录中创建一个文件:

from setuptools import setup, find_packages
setup(name="PACKAGENAME", packages=find_packages())

其中PACKAGENAME是您应用的名称。然后,您必须使用pip安装它:

pip install -e .

-e标志告诉pip以可编辑或“开发”模式安装软件包。因此,下次运行pytest该程序时,它将在标准版本中找到您的应用程序PYTHONPATH


0

我的解决方案:

conftest.pytest包含以下内容的目录中创建文件:

import os
import sys
sys.path.insert(0,os.path.dirname(os.path.realpath(__file__)) + "/relative/path/to/code/")

这会将感兴趣的文件夹添加到python路径,而无需修改每个测试文件,设置env变量或弄乱绝对/相对路径。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.