使用 pytest 的 Python 项目的正确文件夹结构是什么?

What is the correct folder structure to use for a Python project using pytest?

我尝试使用文件夹结构组织我的 Python 项目。当我需要进行测试时,我使用类似下面的东西。

.
|-- src
|   |-- b.py
|   `-- main.py
`-- tests
    `-- test_main.py

这种方法只有一个大问题。 Pytest 不会 运行 如果 main.py 正在导入 b.py.

到目前为止,我已尝试将空的 __init__.py 文件单独或放在一起 srctests 文件夹中,但其中任何一个似乎都有效。

在我看来,这是一个非常标准的项目,但我一直无法在网上找到解决方案。我应该使用不同的文件夹结构吗?有什么推荐的方法可以将 pytest 用于此类项目吗?


这是文件的内容:

# b.py
def triplicate(x):
        return x * 3
# main.py
from b import triplicate

def duplicate(x):
        return x * 2
# test_main.py
from src.main import duplicate


def test_duplicate():
        assert duplicate(2) == 4

这是我在 运行ning pytest:

时得到的错误
==================================================================================================== ERRORS ====================================================================================================
_____________________________________________________________________________________ ERROR collecting tests/test_main.py ______________________________________________________________________________________
ImportError while importing test module 'C:\Users\edwar\test_pytest\tests\test_main.py'.
Hint: make sure your test modules/packages have valid Python names.
Traceback:
c:\python39\lib\importlib\__init__.py:127: in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
tests\test_main.py:1: in <module>
    from src.main import duplicate
src\main.py:1: in <module>
    from b import triplicate
E   ModuleNotFoundError: No module named 'b'
=========================================================================================== short test summary info ============================================================================================
ERROR tests/test_main.py
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! Interrupted: 1 error during collection !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
=============================================================================================== 1 error in 0.15s ===============================================================================================

您可以将源文件放在根目录下,并调整导入路径:

main.py

# main.py
from Whosebug.b import triplicate

def duplicate(x):
        return x * 2

b.py

# b.py
def triplicate(x):
        return x * 3

test_main.py

# test_main.py
from Whosebug.main import duplicate


def test_duplicate():
        assert duplicate(2) == 4

然后从根目录运行pytest .Whosebug:

collected 1 item

tests\test_main.py .                                                                                                                                                                      [100%] 

====================================================================================== 1 passed in 0.03s ======================================================================================= 

或者,如果您希望保留 src 文件夹,您的导入将是这样的:

from Whosebug.src.b import triplicate

Python 使用 'environment variable' PYTHONPATH 来寻找从中导入代码的来源。默认情况下,你执行python程序的目录是自动包含的,但是你想在测试时包含这样的东西:

PYTHONPATH=$PYTHONPATH,../src python test_main.py

这是您从源目录执行测试的情况。像 IntelliJ (PyCharm) 这样的工具可以让您将其作为一个值添加到您的测试调用中。或者,您可以使用 export PYTHONPATH=...。 (请注意,这是针对 *nix 环境的,您在 windows 上的使用情况可能会有所不同。)

结果是 PYTHONPATH 中的每个目录都将被加载,并且 Python 将尝试将其用作您尝试导入的模块的 'root'。您的基本目录结构是最惯用的。

  • 有关正确配置 PYTHONPATH 的更多信息,请参阅 this answer
  • 有关 PYTHONPATH 如何修改和使用的更多信息,请参见 this doc 'under the hood'。
  • 有关在 运行 pytest 测试时包含 src 目录的选项,请参阅 this answer
  • 请参阅 this blog post 关于使用 autoenv(一个 Python 库)启用 .env 文件的使用来为您管理此文件(至少在 virtualenv 设置 - 通常是个好主意)。
  • setup.py 也是包含许多模块的惯用方法,并且可以为您正在处理的情况提供更方便的路径。