Pytest导入机制和 sys.path
/PYTHONPATH
¶
导入模式¶
pytest作为测试框架需要导入测试模块和 conftest.py
执行文件。
在Python中导入文件(至少直到最近)是一个非常重要的过程,经常需要更改 sys.path . 导入过程的某些方面可以通过 --import-mode
命令行标志,可以假定这些值:
prepend
(default): the directory path containing each module will be inserted into the beginning ofsys.path
if not already there, and then imported with the __import__ 内置的。这要求当测试目录树不在包中排列时,测试模块名称必须是唯一的,因为模块将放入
sys.modules
导入后。这是经典的机制,可以追溯到Python2仍然受支持的时候。
append
:包含每个模块的目录追加到sys.path
如果不是已经在那里,并且进口__import__
.这样可以更好地针对已安装的包版本运行测试模块,即使被测试的包具有相同的导入根。例如:
testing/__init__.py testing/test_pkg_under_test.py pkg_under_test/
测试将针对已安装的版本运行
pkg_under_test
什么时候?--import-mode=append
使用while withprepend
他们会选择本地版本。这种混乱是我们提倡使用的原因 src 布局。等同于
prepend
,要求测试模块名称在测试目录树未排列在包中时是唯一的,因为这些模块将放入sys.modules
导入后。importlib
: new in pytest-6.0, this mode uses importlib 导入测试模块。这样就可以完全控制导入过程,而且不需要更改sys.path
或sys.modules
完全。由于这个原因,这根本不要求测试模块名称是唯一的,但也使得测试模块彼此不可导入。对于不在Python包中的测试,这在以前的模式中是可能的,因为更改的副作用
sys.path
和sys.modules
上面提到过。有此要求的用户应该将他们的测试转换成适当的包。我们打算
importlib
未来版本中的默认值。
prepend
和 append
导入模式方案¶
以下是使用时的场景列表 prepend
或 append
pytest需要更改的导入模式 sys.path
为了导入测试模块或 conftest.py
文件,以及用户可能因此而遇到的问题。
测试模块/ conftest.py
包中的文件¶
考虑此文件和目录布局:
root/
|- foo/
|- __init__.py
|- conftest.py
|- bar/
|- __init__.py
|- tests/
|- __init__.py
|- test_foo.py
执行时:
pytest root/
Pytest会找到 foo/bar/tests/test_foo.py
意识到它是一个包的一部分,因为 __init__.py
文件在同一文件夹中。然后它将向上搜索,直到找到最后一个仍包含 __init__.py
文件以查找包 root (在这种情况下) foo/
)要加载模块,它将插入 root/
到前面 sys.path
(如果还没有)以便加载 test_foo.py
作为 模块 foo.bar.tests.test_foo
.
同样的逻辑也适用于 conftest.py
文件:它将作为导入 foo.conftest
模块。
当测试位于包中时,保留完整的包名称非常重要,以避免出现问题并允许测试模块具有重复的名称。这也将在中详细讨论 Python测试发现的约定 .
独立测试模块/ conftest.py
文件夹¶
考虑此文件和目录布局:
root/
|- foo/
|- conftest.py
|- bar/
|- tests/
|- test_foo.py
执行时:
pytest root/
Pytest会找到 foo/bar/tests/test_foo.py
意识到它不是一个包裹的一部分,因为没有 __init__.py
文件在同一文件夹中。然后它将添加 root/foo/bar/tests
到 sys.path
为了进口 test_foo.py
作为 模块 test_foo
. 同样的情况也发生在 conftest.py
添加文件 root/foo
到 sys.path
将其导入 conftest
.
因此,此布局不能具有相同名称的测试模块,因为它们都将在全局导入命名空间中导入。
这也将在中详细讨论 Python测试发现的约定 .
调用 pytest
对战 python -m pytest
¶
使用运行pytest pytest [...]
而不是 python -m pytest [...]
产生几乎相同的行为,除了后者将当前目录添加到 sys.path
,这是标准的 python
行为。