我正在用Python编写一个包。我使用virtualenv。我在virtualenv的.pth路径中设置了模块的根路径,这样我就可以在开发代码和测试时导入包的模块(问题1:这是一种好方法吗?)这很好(这是一个例子,这是我想要的行为):

(VEnvTestRc) zz@zz:~/Desktop/GitFolders/rc$ python
Python 2.7.12 (default, Jul  1 2016, 15:12:24) 
[GCC 5.4.0 20160609] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> from rc import ns
>>> exit()
(VEnvTestRc) zz@zz:~/Desktop/GitFolders/rc$ python tests/test_ns.py 
issued command: echo hello
command output: hello

但是,如果我尝试使用PyTest,我会得到一些导入错误消息:

(VEnvTestRc) zz@zz:~/Desktop/GitFolders/rc$ pytest
=========================================== test session starts ============================================
platform linux2 -- Python 2.7.12, pytest-3.0.5, py-1.4.31, pluggy-0.4.0
rootdir: /home/zz/Desktop/GitFolders/rc, inifile: 
collected 0 items / 1 errors 

================================================== ERRORS ==================================================
________________________________ ERROR collecting tests/test_ns.py ________________________________
ImportError while importing test module '/home/zz/Desktop/GitFolders/rc/tests/test_ns.py'.
Hint: make sure your test modules/packages have valid Python names.
Traceback:
tests/test_ns.py:2: in <module>
    from rc import ns
E   ImportError: cannot import name ns
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! Interrupted: 1 errors during collection !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
========================================= 1 error in 0.09 seconds ==========================================
(VEnvTestRc) zz@zz:~/Desktop/GitFolders/rc$ which pytest
/home/zz/Desktop/VirtualEnvs/VEnvTestRc/bin/pytest

我有点困惑,这似乎表明一个导入错误,但Python做得很好,为什么有一个问题,特别是与PyTest?对原因/补救(问题2)有什么建议吗?我在谷歌上搜索了PyTest的“ImportError: cannot import”错误,但我得到的点击率与丢失的python路径和补救措施有关,这似乎不是这里的问题。有什么建议吗?


当前回答

我的情况:

所有配置都是正确的,除了我忘记删除默认的my_app/tests.py 删除后,python -m pytest tests/my_app可以正常工作 pytest.ini:

[pytest]
DJANGO_SETTINGS_MODULE=necktie_hy.settings
python_files=*/tests/*/test_*.py
python_classes=Test

其他回答

如果你在终端上运行Pytest:

使用——import-mode=append命令行标志运行pytest。

官方文档中的参数说明:https://docs.pytest.org/en/stable/pythonpath.html


乌利希期刊指南: 之前我也写过如果你使用PyCharm如何做同样的事情,但是社区不喜欢扩展答案,所以我删除了可能对有类似问题的人有帮助的额外信息。

如果它与最初在python 2.7中开发,现在迁移到python 3的python代码有关。X认为这个问题可能与进口问题有关。

如。 当从位于同一目录的file: base导入一个对象时,这在python 2.x中是有效的:

from base import MyClass

在python中3。X你应该替换为基本全路径或。base 不这样做会导致上述问题。 所以尝试:

from .base import MyClass

这是一篇中篇文章!描述问题!

问题在于您使用的是哪个pytest以及您使用的是虚拟环境。 如果您在系统范围内(换句话说,在虚拟环境之外)安装了pytest,那么pytest有一个讨厌的习惯,就是只在虚拟环境之外寻找模块!如果您的项目使用的模块只安装在您的虚拟环境中,并且您正在使用系统范围的pytest,那么即使您激活了虚拟环境,它也不会找到该模块

以下是具体步骤

退出任何虚拟环境 使用Pip卸载pytest 激活项目的虚拟环境 在虚拟环境中安装pytest Pytest现在将找到仅虚拟环境的包!

只需在项目根目录中放置一个空的conftest.py文件,因为当pytest发现一个conftest.py时,它会修改sys. py。路径,这样它就可以从conftest模块中导入东西。 一般的目录结构可以是:

Root
├── conftest.py
├── module1
│   ├── __init__.py
│   └── sample.py
└── tests
    └── test_sample.py

另一个建议

我在SO和其他地方探讨了这个问题和其他各种问题……所有关于在项目目录结构的各个部分添加(或删除)一个空__init__.py和/或conftest.py的东西,所有关于PYTHONPATH的东西,等等:这些解决方案都不适合我,在实际上是一个非常简单的情况下,这不应该造成任何痛苦。

I regard this as a flaw in pytest's current setup. In fact I got a message recently from someone on SO who clearly knew his stuff. He said that pytest is not designed to work with (as per Java/Groovy/Gradle) separate "src" and "test" directory structures, and that test files should be mingled in amongst the application directories and files. This perhaps goes some way to providing an explanation ... however, tests, particularly integration/functional tests, don't always necessarily correspond neatly to particular directories, and I think pytest should give users more choice in this regard.

项目结构:

project_root
    src
        core
            __init__.py
            __main__.py
            my_other_file.py 
    tests
        basic_tests
            test_first_tests.py

导入问题提出:非常简单,__main__.py有一行import my_other_file。当我只是运行应用程序时,这(并不奇怪)工作正常,即从根目录运行python src/core。

但是当我用导入__main__.py的测试运行pytest时,我得到

ModuleNotFoundError: No module named 'my_other_file'

在__main__.py中试图导入“my_other_file”的行中。请注意,在我的例子中,这里的问题是,在pytest测试过程中,一个应用程序文件无法在同一个包中找到另一个应用程序文件。

使用PYTHONPATH环境 经过大量的实验,并将__init__.py文件和confest.py文件放在我能找到的几乎每个目录中(我认为关键文件是__init__.py添加到“tests”和“basic_tests”中,参见上面的目录结构),然后将PYTHONPATH设置为如下所示

PYTHONPATH=D:\My Documents\software projects\EclipseWorkspace\my_project\src\core

... 我发现它很有效。在测试文件中的导入必须进行一些调整,一般模式是从核心导入app, project,但测试文件能够“看到”核心,关键是没有必要在应用文件中的导入命令中添乱。

然而……由于某种原因,使用此方法的测试现在运行得慢得多!下面的解决方案只加载了一次核心包,与之相比,我怀疑PYTHONPATH解决方案可能会导致大量代码被一次又一次地重新加载。我还不能证实这一点,但我看不出任何其他解释。

另一种选择 我的另一个相当简单的解决方案是:

1 -在应用程序包的__init__.py中(即“core”目录),放入以下两行:

import pathlib, sys
sys.path.append(str(pathlib.Path(__file__).parent))

注意,通常__init__.py中没有任何东西。事实证明,正如我通过实验证实的那样,pytest通常(参见下面的更新)在这种情况下执行__init__.py,在pytest完成了它所做的任何事情之后。路径条目。

2-更新2022-01: 我发现的原始解决方案涉及在应用程序目录(ies)中放置一个conftest.py文件-没有它就不能工作。这显然是不可取的。我发现另一个解决方案是把这段代码放在你根目录下的conftest.py文件中:

def pytest_configure(config):
    import src.core # NB this causes `src/core/__init__.py` to run
    # set up any "aliases" (optional...)
    import sys
    sys.modules['core'] = sys.modules['src.core']

... 实际上,从我的实验来看,将conftest.py放在应用程序目录中的效果似乎是pytest随后在该目录中运行__init__.py。这似乎暗示模块正在被导入…

(以前的建议:) 是的,你还必须在“core”目录下包含一个空的“conftest.py”文件。希望这应该是你唯一需要的conftest.py:我对所有这些都进行了试验,将一个放在根目录中是不必要的(如果没有__init__.py中建议的代码,它也不能解决问题)。

3 -最后,在我的测试函数,之前调用核心。__main__在我的例子中,我必须导入我知道即将被导入的文件:

import core.my_other_file
import core.__main__ 

如果您在文件中的第一个测试中执行此操作,您将发现sys。为该文件中的所有其他测试设置了Modules。更好的是,输入import core。My_other_file在文件的最开始,在第一个测试之前。不幸的是,from core import *似乎不起作用。

后来:这种方法有一些特点和局限性。例如,虽然-k开关可以过滤入/出测试或整个文件,但如果您执行类似pytest tests/tests_concerning_module_x之类的操作,似乎core.__init__.py没有得到运行…因此,在测试期间,核心模块中的文件再次相互不可导入。其他限制可能会被揭示出来。


正如我所说,我认为这是pytest设置中的一个缺陷。我完全不知道pytest做了什么来为sys. net建立一个常识性的设置。路径,但它显然是错的。不应该依赖PYTHONPATH或其他方法,如果这确实是“官方”解决方案,那么关于这个主题的文档非常缺乏。

注意我的这个建议有一个问题:通过添加到sys。当pytest每次在一个模块中运行__init__.py时,它意味着这个新路径此后在sys. py中成为永久路径。路径,在测试期间,更令人担忧的是,在应用程序本身的运行期间,如果有任何东西实际上调用__init__.py。(顺便说一句,只是使用python src/core(就像我的例子中那样)不会导致这种情况发生。但其他事情可能会。)

为了解决这个问题,我有一个笨拙但有效的解决方案:

import pathlib, sys, traceback
frame_list = traceback.extract_stack()
if len(frame_list) > 2:
    path_parts = pathlib.Path(frame_list[2].filename).parts
    if len(path_parts) > 2:
        module_dir_path_str = str(pathlib.Path(__file__).parent)
        if sys.platform.startswith('win'):
            if path_parts[-3:-1] == ('Scripts', 'pytest.exe'):
                sys.testing_context = True
                sys.path.append(module_dir_path_str)
        elif sys.platform.startswith('lin'):
            if path_parts[-3:-1] == ('_pytest', 'config'):
                sys.testing_context = True
                sys.path.append(module_dir_path_str)

这是基于我对pytest在W10上下文中和Linux Mint 20上下文中运行某些东西时的堆栈跟踪的检查。这意味着在应用程序运行时,不会混淆sys.path。

当然,这可能会与未来的pytest版本有所不同。我的版本是6.2.5。