我使用easy-install在Mac上安装pytest,并开始为具有如下文件结构的项目编写测试:
1 2 3 4 5 6
| 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。
是否需要编辑路径以在这些系统上运行py.test?有人经历过吗?
- 以下是使用设置工具修复它的方法。
- 请检查@hoefring答案,并考虑更改您接受的答案,如果允许的话,在这段时间之后:更好!
- 好问题标题
我不知道为什么py.test不在pythonpath本身中添加当前目录,但这里有一个解决方法(从存储库的根目录执行):
。
它工作是因为python为您添加了pythonpath中的当前目录。
- 这是一个非常好的解决方法,例如,测试不可打包/安装的脚本和模块!
- 如果您有运行应用程序的代码,而不是在执行命令的级别上,则需要将相对导入重写为绝对导入。例如:project/test/all-my-tests和project/src/app.py,由于这个变化,我们需要间接调用app.py,使用project/src中的__main__.py文件,这样我们就可以调用python -m src。据我所知,相当混乱。
- @Zelphir:使用绝对进口是一种推荐做法。Habnabit有一篇关于包装最佳实践的好文章:blog.habnab.it/blog/2013/07/21/python-packages-and-you,pep8说"隐式相对导入不应该在python 3中使用,并且已经在python 3中删除了。"请参见:python.org/dev/pep s/pep-0008。
- @你的意思是"绝对计划"对吗?因为像/home/user/dev/projectxyz/src ...这样的东西会很糟糕,在大多数情况下不会在其他机器上运行。我想我的意思是,我必须始终将整个项目根目录写入模块路径,即使模块与文件运行在同一文件夹中。我不知道这是最佳实践,所以这是一个有用的信息,谢谢。我同意大多数的PEP8,尽管它仍然不完美。
- @泽菲尔,是的,这就是我的意思。我相信python中的绝对导入一词总是指"绝对项目"。参见:python.org/dev/peps/pep-0328/绝对进口的理由‌&8203;。事实上,我很确定您不能从随机的绝对路径位置导入,至少不能使用默认的"导入"机制。
- 我在测试中添加了__init__.py,解决了这个问题。现在我可以使用pytest
我也有同样的问题。我通过在我的tests目录中添加一个空的__init__.py文件来修复它。
- 注意,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/goodpractices.html不建议这样做。
- 我带着同样的问题来到这里,发现从我的测试目录中删除__init__.py解决了这个问题。
- @k.-michaelaye如果测试目录不是包,您应该如何导入测试中的模块??
- @黑手党,我没看到问题?测试不必是可导入的代码,它们是由测试运行程序发现的。只有要测试的代码才应该是已安装的包/模块,而不是测试。
- @K.-Michaelaye我相信我的问题源于使用相对导入-测试代码找不到要测试的应用程序代码
- 测试代码不应该使用相对导入,是的。从导入风格上讲,您应该考虑将测试代码不作为包的一部分,而只是通过安装的包名称导入所有内容。但是,我仍然看不到,理论上如何通过将测试代码作为包来解决这一问题?
- 在test/的子目录中添加一个__init__.py使在该子目录中针对要安装的模块运行特定测试具有绝对的导入功能。谢谢。
- @K.-迈克尔·林克死了
- 好了:doc.pytest.org/en/latest/goodpractices.html在谷歌上很容易找到。
- 这是最优雅的解决方案,它使测试中的导入行为与在代码中的行为相同。
- 从docs.pytest.org/en/latest/goodpractices.html(其中最新的==pytest-4.0当前)可以看出,测试路径中的__init__.py文件不再被弃用。如果您有更大的测试主体并且想要模块化您的代码(是的,测试代码!).
是的,如果您在测试目录中使用cd,那么源文件夹不在python的路径中。
您有两个选择:
手动将路径添加到测试文件,如下所示:
1 2 3
| import sys, os
myPath = os.path.dirname(os.path.abspath(__file__))
sys.path.insert(0, myPath + '/../') |
使用env var PYTHONPATH=../运行测试。
- 我什么时候到目录去?我正在从我的根目录运行py.test。除非我弄错了,你是说当Pytest浏览我的文件夹时
- 如果这是一个cd问题,我会不会在mac上打它?
- 哦,我误读了,认为它在测试目录中不起作用。建议1中的技巧仍然有效。我只使用Linux,因此无法解释其他操作系统上的行为。
- 在所有test.py文件上都有类似的导入吗?
- 是的,但是我的目录结构通常略有不同-我通常将/src和/test保存在根目录下。
- 有没有一个最佳的实践,以一个修改后的pythonpath初步测试?我只想编写py.test,它使用一些配置文件(我在一些项目根目录中看到了pytest.py文件)。
conftest解决方案
微创解决方案是在repo/目录中添加一个名为conftest.py的空文件:
1
| $ touch repo/conftest.py |
。
就这样。不需要编写用于管理sys.path的自定义代码,也不需要记住拖动PYTHONPATH或将__init__.py放入不属于它的目录中。
之后的项目目录:
1 2 3 4 5 6 7
| repo
├── conftest.py
├── app.py
├── settings.py
├── models.py
└── tests
└── test_app.py |
解释
pytest在测试集合中查找conftest模块,以收集自定义挂钩和固定装置,为了从中导入自定义对象,pytest将conftest.py的父目录添加到sys.path中。
其他项目结构
如果您有其他项目结构,请将conftest.py放在包根目录中(包含包但不是包本身,因此不包含__init__.py的目录),例如:
1 2 3 4 5 6 7 8 9 10 11 12
| 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.py放在src目录中):
1 2 3 4 5 6 7 8 9 10 11 12 13
| repo
├── src
│ ├── conftest.py
│ ├── spam
│ │ ├── __init__.py
│ │ ├── bacon.py
│ │ └── egg.py
│ └── eggs
│ ├── __init__.py
│ └── sausage.py
└── tests
├── test_bacon.py
└── test_egg.py |
当心添加src到PYTHONPATH会降低src布局的意义和好处!最后,您将从存储库测试代码,而不是从已安装的包进行测试。如果你需要这样做,也许你根本不需要src目录。
从这里到哪里
当然,conftest模块不仅仅是一些帮助源代码发现的文件;它是pytest框架的所有特定于项目的增强和您的测试套件的定制发生的地方。pytest有很多关于分散在文档中的conftest模块的信息;从conftest.py开始:本地目录插件
另外,在conftest模块上还有一个很好的问题:在py.test中,conftest.py文件的用途是什么?
- 这真的应该是公认的答案,其他的都是笨拙的解决方法。
- @戴维德,谢谢,很高兴这个食谱值得分享!
- 非常感谢,这是在vs代码内部的固定测试!
- 这不管用,就这么简单。尝试将文件放在根目录,然后从测试中导入。将pythonpath设置为根可以正常工作,这种黑客攻击根本没有帮助。
- @aaa9021虽然我不能复制您的问题(从根目录中的conftest导入在任何级别上都有效),但您不应该从conftest文件导入,因为它是pytest的保留名称,强烈建议不要这样做。通过这样做,你可以为将来的错误埋下种子。创建另一个名为utils.py的模块,并将用于重用的代码放在测试中。
- 竖起大拇指!这是唯一对我有效的解决方案。
- 由于某些原因,在安装程序包时,Travisci上会导致moduleNotfoundError。所以当心!
- @Jarno你能给受影响的存储库一个链接吗?如果通过更改sys.path获得ModuleNotFound,则表示项目/导入布局(最可能是名称阴影)中存在错误。
- @霍弗林不幸的是,该项目目前是私有的。但后来它在本地运行,但在Travis上失败了。我还涉及到了嵌套模块。我可以尝试想出一个最小的非工作的例子。
- @Jarno你也可以问一个新问题,把链接放在评论里,我很乐意帮忙。
可以在项目根目录中使用pythonpath运行
号
或者使用pip安装作为可编辑导入
1
| pip install -e . # install package using setup.py in editable mode |
- 对于不在src目录结构中的test目录以及从包含test和src目录的目录调用的我来说,这不起作用。
以模块形式运行pytest,其中:埃多克斯1〔9〕
- 这是最好的解决方案。谢谢!
- 这似乎是一个可行的解决方案,但有人能解释为什么吗?我宁可解决根本原因,也不只是使用python -m pytest,除了"因为它有效"之外,没有任何解释。
- 例如,当项目层次结构为:package/src package/tests和从src导入的tests时,就会发生这种情况。作为模块执行会将导入视为绝对导入,而不是相对于执行位置。
- 这个解决方案帮了我,谢谢!这是因为Python版本中的冲突。pytest测试适用于早期的python版本。在我的情况下,我的python版本是3.7.1,python-m pytest测试有效,但pytest测试无效。
我创建这个是为了回答你的问题和我自己的困惑。希望有帮助。注意py.test命令行和tox.ini中的pythonpath。
https://github.com/jeffmacdonald/pytest_测试
具体来说:你必须告诉py.test和tox在哪里找到你所包含的模块。
使用py.test,可以执行以下操作:
号
在tox.ini中加入这个:
1 2 3 4 5
| [testenv]
deps= -r{toxinidir}/requirements.txt
commands=py.test
setenv =
PYTHONPATH = {toxinidir} |
。
- 你能简单解释一下你所链接的项目吗?
- 也许只有我一个人,但是这个项目的自述文件非常详细,我对stackoverflow的评论说明了我为什么创建repo。
- 虽然并非严格必要,但通常的政策是在答案本身中包含答案的主要内容,因为它确保了从现在起的x年内,当链接的资源可能早已消失时,答案是可以理解的。
- 公平点。我会更新的。
- 谢谢(仅供参考:我没有投你反对票)。
- :)哦,好吧。那是你的互联网。
当我意外地将__init__.py文件添加到src目录(不应该是python包,只是一个包含所有源代码的容器)时,我开始遇到奇怪的ConftestImportFailure: ImportError('No module named ...错误。
我得到这个错误是因为一些更简单的事情(你甚至可以说是微不足道的)。我没有安装pytest模块。所以一个简单的apt install python-pytest为我修理了它。
"py test"将作为测试依赖项列在setup.py中。确保您也安装了测试需求。
我也有类似的问题。pytest无法识别安装在我工作环境中的模块。
我还将pytest安装到相同的环境中,解决了这个问题。
我在使用相对导入时出错。在OP示例中,test_app.py应使用例如
然而,大量的init_uuuuuy文件分散在文件结构周围,这不起作用,并且会产生一种重要的错误,除非文件和测试文件在同一个目录中。
号
下面是一个例子,说明了我与我的一个项目之间的关系:
以下是我的项目结构:
1 2 3
| microbit/
microbit/activity_indicator/activity_indicator.py
microbit/tests/test_activity_indicator.py |
为了能够从test_activity_indicator.py访问activity_indicator.py,我需要:
- 使用正确的相对导入启动test_activity_indicative.py:
1
| from microbit.activity_indicator.activity_indicator import * |
。
- 在整个项目结构中放置uuu init_uuuu.py文件:
1 2 3 4 5 6
| 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 |
对我来说,问题是django生成的tests.py和tests目录。删除tests.py解决了这个问题。