为什么相对路径在 python 测试中不起作用?

Why is relative path not working in python tests?

我的目录布局如下

project\
project\setup.py
project\scripts\foo.py
project\scripts\bar.py
project\scripts\__init__.py
project\tests\test_foo.py
project\tests\__init__.py

我的测试文件如下所示

project\tests\test_fo.py

from ..scripts import foo

def test_one():
     assert 0

当我执行

时出现以下错误
cd C:\project
C:\virtualenvs\test_env\Scripts\activate
python setup.py install
python setup.py test

E ValueError:尝试相对导入超出顶级包

我做错了什么? 这是我的 setup.py

setup(
    name = 'project',
    setup_requires=['pytest-runner'],
    tests_require=['pytest'],
    packages = ["scripts","tests"],
    package_data={
          'scripts': ['*.py'],
          'tests': ['*.py'],
         },
)

相对导入仅在 包内有效。 scripts 可能是一个包,tests 也是,但是 project 目录 不是 (也不应该是)。这使得 scriptstests 顶级包 。您不能使用相对语法引用其他顶级名称。

此外,测试不是 运行 tests 包;测试 运行ner 导入 test_foo 模块,而不是 tests.test_foo 模块,所以 就 Python 而言 test_foo 是顶级模块。

scripts 是顶级名称,直接使用即可。但是,您必须将 project 目录添加到 sys.path。您可以在 test_foo.py 文件的顶部执行此操作:

import os
import sys

TEST_DIR = os.path.dirname(os.path.abspath(__file__))
PROJECT_DIR = os.path.abspath(os.path.join(TEST_DIR, os.pardir))
sys.path.insert(0, PROJECT_DIR)

然后使用绝对路径从 scripts 导入:

from scripts import foo

但是请注意,当您 运行 python setup.py 时,您当前的工作目录无论如何都会添加到 sys.path,因此 scripts 可以直接使用,而无需 [=74] =] 与 sys.path.

此外,pytest 已经 为您完成这项工作;对于任何给定的测试文件,它将确保第一个包含 no __init__.py 文件的父目录位于 sys.path 上。在您的情况下,这是 project/ 目录,因此再次 scripts 可直接从中导入。见 Good Practices:

If pytest finds a “a/b/test_module.py” test file while recursing into the filesystem it determines the import name as follows:

  • determine basedir: this is the first “upward” (towards the root) directory not containing an __init__.py. If e.g. both a and b contain an __init__.py file then the parent directory of a will become the basedir.
  • perform sys.path.insert(0, basedir) to make the test module importable under the fully qualified import name.
  • import a.b.test_module where the path is determined by converting path separators / into ”.” characters. This means you must follow the convention of having directory and file names map directly to the import names.

请注意,为了在使用 setup.py test 时实际使用 pytest 到 运行 测试,您需要在 setup.cfg 文件中注册一个别名(创建它在 project/ 如果你没有):

[aliases]
test = pytest