使用 pytest 正确导入
Posted
技术标签:
【中文标题】使用 pytest 正确导入【英文标题】:Importing correctly with pytest 【发布时间】:2014-11-07 17:55:24 【问题描述】:我刚刚准备好在 Python 2.6 中使用 pytest。到目前为止,除了处理“import”语句外,它运行良好:我似乎无法让 pytest 以与我的程序相同的方式响应导入。
我的目录结构如下:
src/
main.py
util.py
test/
test_util.py
geom/
vector.py
region.py
test/
test_vector.py
test_region.py
要运行,我从 src/ 调用 python main.py
。
在 main.py 中,我同时导入了向量和区域
from geom.region import Region
from geom.vector import Vector
在vector.py中,我用
导入区域from geom.region import Region
当我在标准运行中运行代码时,这些都可以正常工作。但是,当我从 src/ 调用“py.test”时,它始终会因导入错误而退出。
一些问题和我的解决方案尝试
我的第一个问题是,在运行“test/test_foo.py”时,py.test 无法直接“导入 foo.py”。我通过使用“imp”工具解决了这个问题。在“test_util.py”中:
import imp
util = imp.load_source("util", "util.py")
这适用于许多文件。这似乎也暗示了当pytest运行“path/test/test_foo.py”来测试“path/foo.py”时,它是基于目录“path”的。
但是,“test_vector.py”会失败。 Pytest 可以找到并导入 vector
模块,但它无法定位 vector
的任何导入。以下导入(来自“vector.py”)在使用 pytest 时均失败:
from geom.region import *
from region import *
这些都给出了形式的错误
ImportError: No module named [geom.region / region]
我不知道下一步该怎么做才能解决这个问题;我对 Python 中的导入的理解有限。
使用 pytest 时处理导入的正确方法是什么?
编辑:极其骇人的解决方案
在vector.py
中,我将导入语句从
from geom.region import Region
简单
from region import Region
这使得导入相对于“vector.py”的目录。
接下来,在“test/test_vector.py”中,将“vector.py”的目录添加到路径中,如下:
import sys, os
sys.path.append(os.path.realpath(os.path.dirname(__file__)+"/.."))
这使 Python 能够从“geom/test/test_vector.py”中找到“../region.py”。
这可行,但它似乎非常有问题,因为我在路径中添加了大量新目录。我正在寻找的是
1) 与pytest兼容的导入策略,或者
2) pytest 中的一个选项,使其与我的导入策略兼容
所以我将这个问题留给这些类型的答案。
【问题讨论】:
我仍在寻找兼容 py.test 的导入策略:/ @Zelphir python -m pytest tests/ 工作正常。 @ederag 如果我这样做,在我的项目中,python 找不到测试用例使用的所有其他导入(或测试用例中使用的导入类/模块)。如果我将它们更改为绝对导入,我可以使用您的命令运行测试,但是当我尝试运行程序时它没有找到导入 - 导入语句的无休止循环更改,我不知道如何修复它. 在你的主包中也推荐绝对导入。请参阅***.com/q/4209641/3565696 的答案。但请记住在您的目录中添加__init__.py
文件,以创建regular package。 (我从未尝试过使用命名空间包)
您提到,“当我在标准运行中运行代码时,这些都可以正常工作。”你能准确描述一下你是如何开始“标准运行”的吗?这将让我们弄清楚在“标准运行”开始时如何配置 sys.path
。
【参考方案1】:
如果您在测试目录中包含 __init__.py 文件,那么当程序要设置主目录时,它会“向上”移动,直到找到不包含 init 的目录文件。在这种情况下 src/.
从这里你可以通过说导入:
from geom.region import *
您还必须确保在任何其他子目录中都有一个 init 文件,例如其他嵌套的测试目录
【讨论】:
这是一个正确的答案(而且真的很短!)!只需在测试目录中创建一个空的__init__.py
并说from import geom.region import *
【参考方案2】:
这里的问题是 Pytest 遍历文件系统以发现包含测试的文件,但随后需要生成一个模块名称,这将导致 import
加载该文件。 (记住,files are not modules。)
Pytest 提出了这个test package name,方法是找到不包含__init__.py
文件的文件级别或之上的第一个目录,并声明包含由此生成的模块的模块树的“basedir”文件。然后它将 basedir 添加到 sys.path
并使用模块名称导入,该模块名称将找到相对于 basedir 的文件。
这有一些你应该注意的含义:
基本路径可能与您预期的基本路径不匹配,在这种情况下,模块的名称将与您通常使用的名称不匹配。例如,在 Pytest 运行期间,您认为的 geom.test.test_vector
实际上将被命名为 test_vector
,因为它在 src/geom/test/
中找不到 __init__.py
,因此将该目录添加到 sys.path
。
如果不同目录中的两个文件具有相同的名称,您可能会遇到模块命名冲突。例如,在任何地方缺少__init__.py
文件,添加geom/test/test_util.py
将与test/test_util.py
冲突,因为两者都加载为import test_util.py
,路径中同时包含test/
和geom/test/
。
您在这里使用的系统,没有明确的__init__.py
模块,是让Python 为您的目录创建implicit namespace packages。 (一个包是一个带有子模块的模块。)理想情况下,我们会为 Pytest 配置一个路径,它也会从中生成这个,但它似乎不知道如何做到这一点。
这里最简单的解决方案就是将空的__init__.py
文件添加到src/
下的所有子目录中;这将导致 Pytest 使用以 src/
下的目录名称开头的包/模块名称导入所有内容。
问题How do I Pytest a project using PEP 420 namespace packages? 讨论了其他解决方案。
【讨论】:
我的问题是测试文件夹中缺少__init__.py
。谢谢。
似乎每个测试文件夹(任何级别)都应该有一个__init__.py
。
我也遇到了这个__init__.py
无处不在的问题:D LOL 将__init__.py
放在tests/
文件夹中救了我!
感谢您的精彩解释,我终于明白__init__.py
文件应该放在哪里以及为什么!!
在我的情况下,在单元测试文件夹下添加 init.py 后它开始工作,有人可以解释这种行为的原因吗?【参考方案3】:
我也想知道如何解决这个问题。在阅读了这篇文章并玩了一下之后,我想出了一个优雅的解决方案。我创建了一个名为“test_setup.py”的文件并将以下代码放入其中:
import sys, os
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
我把这个文件放在顶层目录(比如src)。从***目录运行pytest
时,它将运行包括此文件在内的所有测试文件,因为该文件以“test”为前缀。该文件中没有测试,但它仍然运行,因为它以“test”开头。
代码会将 test_setup.py 文件的当前目录名称附加到测试环境中的系统路径中。这只会执行一次,因此不会在路径中添加一堆东西。
然后,在任何测试函数中,您可以导入与该***文件夹相关的模块(例如import geom.region
),并且它知道在哪里可以找到它,因为 src 目录已添加到路径中。
如果您想运行单个测试文件(例如 test_util.py)而不是所有文件,您可以使用:
pytest test_setup.py test\test_util.py
这会同时运行 test_setup 和 test_util 代码,以便仍然可以使用 test_setup 代码。
【讨论】:
【参考方案4】:import 在以下目录中查找模块:
-
程序的主目录。这是您的根脚本的目录。当您运行 pytest 时,您的主目录就是它的安装位置(可能是 /usr/local/bin)。无论您是从 src 目录运行它,因为 pytest 的位置决定了您的主目录。这就是它找不到模块的原因。
PYTHONPATH。这是一个环境变量。您可以从操作系统的命令行进行设置。在 Linux/Unix 系统中,您可以通过执行:'export PYTHONPATH=/your/custom/path' 如果您希望 Python 从测试目录中找到您的模块,您应该在此包含 src 路径变量。
标准库目录。这是安装所有库的目录。
使用 pth 文件的选项不太常见。
sys.path 是 主目录、PYTHONPATH 和 标准组合的结果库目录。你在做什么,修改 sys.path 是正确的。这是我经常做的事情。如果您不喜欢弄乱 sys.path
,可以尝试使用 PYTHONPATH【讨论】:
谢谢!这澄清了事情。对我使用的解决方案进行了轻微更改(如上所示的编辑)以上是关于使用 pytest 正确导入的主要内容,如果未能解决你的问题,请参考以下文章