Pytest: how to work around missing __init__.py in the tests folder?
我将所有pytest文件存储在项目根目录下的tests子目录中。 该目录或更高目录中没有__init__.py,并且pytest tests/test_a.py可以正常工作。 我也可以直接从tests文件夹中运行pytest test_a.py。
1 2 3 4 5 6
| |--<project_root>
+---[tests]
test_a.py
base.py
config.py
conftest.py |
test_a.py中的测试类继承自base.py。 现在的问题是,由于缺少__init__.py,IDE工具无法正常工作,并且无法解决导入问题。 在tests下添加__init__.py可解决IDE中的所有导入错误,但是由于py.test无法导入conftest.py,因此测试将不再通过pytest test_a.py运行。
在我的conftest.py中,我具有以下导入:
1
| from config import HOST_URL_DEFAULT, USER, PASSWORD |
结果是:
1 2
| ModuleNotFoundError: No module named 'config'
ERROR: could not load /project_root/tests/conftest.py |
有没有一种方法可以解决此问题,以便IDE工具都能正常工作并且pytest可以正常工作? 如果可能的话,我想避免使用点导入。
更新:阅读此答案后,我认为我终于开始更多地了解python导入的工作原理。
-
为什么pytest无法导入config.py?
-
@MisterMiyagi:是pytest无法导入的conftest.py,对错。 我没有在测试代码中的任何地方显式导入conftest.py,但是文件存在并且正在使用它。 我更新了问题以显示究竟是什么引起了错误。
-
因此,问题出在您的IDE而不是pytest? 也许可以告诉IDE tests是项目设置中某处的名称空间包。 如果将__init__.py放在tests目录中,则config模块的限定名称将更改为tests.config,并且必须按照答案的建议调整导入。 一种或另一种方式,可能值得查看《良好集成规范》,以获取有关不同测试布局的提示。
-
至少在PyCharm中,您可以在项目设置中将tests添加为源根目录。 这样可以解决导入问题。
添加__init__.py并在conftest.py中使用相对或绝对导入:
1 2 3 4
| # relative
from .config import HOST_URL_DEFAULT, USER, PASSWORD
# absolute
from tests.config import HOST_URL_DEFAULT, USER, PASSWORD |
在一个包中(由__init__.py标识),您需要明确的导入路径。使用不合格的导入(from config import ...)取决于PYTHONPATH或sys.modules,包括您的软件包根目录-这通常不可靠,应避免使用,因为它会绕过软件包基础结构。
您这里拥有的(其他模块使用的config.py)是一个软件包。将其视为一个。
pytest不会阻止将测试包与__init__.py一起使用!确实有很多用例-例如您的。
pytest阻止的是在同一源根目录中包含源程序包和测试程序包。但是,这意味着您应该移动源软件包,而不是测试软件包!
1 2 3 4 5 6 7
| mysource/ # root directory for both mypackage and mytests
+- mypackage/ # should be src/mypackage instead!
+- __init__.py
+- abcd.py
+- mytests
+- __init__.py
+- tests_abcd.py # ``from mypackage import abcd`` imports from source |
问题是运行mytests意味着mypackage也可以从源导入。也就是说,您的测试适用于您的源包,而不适用于已安装的包。如果您的安装过程中有任何错误,测试将无法捕获。有关详细信息,请参见pytest文档中链接的博客。
-
我的理解是pytest不鼓励在测试中使用__init__.py,这确实设法在不将所有内容打包的情况下运行了代码。
-
pytest不需要您将非软件包转换为软件包。 不过,您已经在那儿有了一个包裹。
-
@ccpizza我已经添加了一些进一步的描述,即pytest不鼓励在测试包旁边放置一个源包,而不是一个测试包本身。