另一个建议
我在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。