我正在用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路径和补救措施有关,这似乎不是这里的问题。有什么建议吗?


当前回答

[解析] 在直接进入删除/添加__init__.py的解决方案之前,我们可能还想看看在你的类中是如何导入的。实际上,我花了一天的时间在__init__.py上,认为这可能是问题所在:)然而,这是相当有意义的。

在我的例子中,从一个python类调用类到另一个python类的方式是错误的,这抛出了ImportError。修正了类/模块被调用的方式,它像魅力一样工作。希望,这也能帮助其他人。

是的,对于类似的错误,我们可能有不同的解决方案,这取决于代码的编写方式。最好在自我调试上花更多的时间。经验教训:)快乐的编码!!

其他回答

我的情况:

所有配置都是正确的,除了我忘记删除默认的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

我不能说我理解为什么这样工作,但我有同样的问题,如果我运行python -m pytest,测试工作正常。

我在一个virtualenv中,pytest也在全局范围内可用:

(proj)tom@neon ~/dev/proj$ type -a python
python is /home/tom/.virtualenvs/proj/bin/python
python is /usr/bin/python

(proj)tom@neon ~/dev/proj$ python -V
Python 3.5.2

(proj)tom@neon ~/dev/proj$ type -a pytest
pytest is /home/tom/.virtualenvs/proj/bin/pytest
pytest is /usr/bin/pytest

(proj)tom@neon ~/dev/proj$ pytest --version
This is pytest version 3.5.0, imported from /home/tom/.virtualenvs/proj/lib/python3.5/site-packages/pytest.py

当我试图在我的测试文件中导入一个新类时,我遇到了相同/类似的问题。在我的例子中,我将新导入移动到测试文件中的最后一个导入,然后问题就消失了。但不完全确定原因。

在我的例子中,发生导入错误是因为包指向具有相同名称的另一个包/目录,其路径比我实际需要的文件夹高一级。 我认为这也解释了为什么有些人需要删除_ init _.py,而另一些人需要添加回来。

我只是把print(the_root_package.__path__)(在导入the_root_package之后)放在python控制台和pytest脚本中来比较差异

底线:当您使用python时,您导入的包可能与运行pytest时的包不同。

另一个建议

我在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。