【问题标题】:Why is relative path not working in python tests?为什么相对路径在 python 测试中不起作用?
【发布时间】:2016-11-08 22:42:26
【问题描述】:

我的目录布局如下

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: Attempted relative import beyond toplevel package

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

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

【问题讨论】:

    标签: python relative-path setuptools pytest setup.py


    【解决方案1】:

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

    此外,测试不使用tests 包运行;测试运行程序导入 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 可以直接使用,而无需摆弄sys.path

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

    如果pytest 在递归到文件系统时找到“a/b/test_module.py”测试文件,它会按如下方式确定导入名称:

    • 确定basedir:这是第一个不包含__init__.py 的“向上”(朝向根)目录。如果例如a 和 b 都包含一个__init__.py 文件,那么 a 的父目录将成为 basedir。
    • 执行sys.path.insert(0, basedir) 使测试模块可在完全限定的导入名称下导入。
    • import a.b.test_module 其中路径是通过将路径分隔符 / 转换为“.”来确定的。人物。这意味着您必须遵循将目录和文件名直接映射到导入名称的约定。

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

    [aliases]
    test = pytest
    

    【讨论】:

    • 将其更改为 from scripts import foo 后出现此错误 (test_123) D:\Projects\project>python setup.py test running test running egg_info writing project.egg-info\PKG-INFO将顶级名称写入 project.egg-info\top_level.txt 将 dependency_links 写入 project.egg-info\dependency_links.txt 读取清单文件 'project.egg-info\SOURCES.txt' 写入清单文件 'project.egg-info \SOURCES.txt' 运行 build_ext ------------------------------------------- --------------------------------------- 在 0.000 秒内运行 0 次测试 OK
    • @user330612:我明天去看看。
    • @user330612:请注意我的回答中的良好实践链接;那里有关于在使用pytest 运行测试之前必须向setup.cfg 文件添加别名的信息。
    • @user330612: 没有setup.cfg 设置别名,不会运行测试,使用别名,pytest 会发现tests 中的测试并运行它们。
    • @alial-karaawi:关键是你不需要这样做,而不是在你使用 pytest 时。
    猜你喜欢
    • 2021-04-12
    • 2014-10-08
    • 2013-03-21
    • 1970-01-01
    • 2012-03-10
    • 1970-01-01
    • 2017-07-12
    • 1970-01-01
    • 2013-10-22
    相关资源
    最近更新 更多