Python中使用unittest进行相对导入

45

我正在尝试使用Python的unittest和相对导入,但我似乎无法弄清楚。我知道有很多相关的问题,但到目前为止都没有帮助过我。如果这是重复的问题,那么很抱歉,但我真的很感谢任何帮助。我试图使用PEP 328中的语法http://www.python.org/dev/peps/pep-0328/,但我一定是做错了什么。

我的目录结构是:

project/
    __init__.py
    main_program.py
    lib/
        __init__.py
        lib_a
        lib_b
    tests/
        __init__.py
        test_a
        test_b

我使用以下方式运行测试:

python -m unittest test_module1 test_module2

test_a需要同时导入lib/lib_a和main_program。这是我尝试使用的来自test_a的代码:

from ..lib import lib_a as lib
from ...project import main_program

两者都会引发此错误:

ValueError: Attempted relative import in non-package

我所有的 init.py 文件目前都是空的。

如果有任何具体建议,将不胜感激!!

编辑:

这可能是答案:Python Packages? 我仍在验证它是否有效。

编辑 II:

澄清一下,在这一点上,我已经尝试以3种不同的方式运行我的测试文件:

project/tests $ python -m unittest test_a
project/tests $ python -m test_a
project/tests $ ./test_a

所有三个方法都出现了与上述相同的错误。当我在项目目录中使用相同的三种语法时,我会得到这个错误:

ValueError: Attempted relative import beyond toplevel package

再次感谢。


请注意,虽然最后一种情况是合法的,但它肯定是不被鼓励的("疯狂"是Guido使用的词)。- PEP328 - John Mee
1
尝试将其作为一个包调用... https://dev59.com/HGgu5IYBdhLWcg3wJT9I#11536794 - John Mee
1
可能是重复的问题:即使有__init__.py,仍在非包中尝试相对导入 - John Mee
我需要通过单元测试来运行它,所以我认为那样行不通。当我运行这个命令:'python -m tests.test_a'时,我会得到这个错误:'ValueError: Attempted relative import beyond toplevel package'。 - J Jones
我建议按照 http://blog.habnab.it/blog/2013/07/21/python-packages-and-you/ 中描述的方式布置项目结构。 - Brave Sir Robin
显示剩余3条评论
3个回答

27

根据我的经验,如果您的项目根目录不是一个包,那么最容易的方法是这样的:

project/
  test.py
  run.py
  package/
    __init__.py
    main_program.py
    lib/
      __init__.py
      lib_a
      lib_b
    tests/
      __init__.py
      test_a
      test_b

然而,从 python 3.2 开始,unittest 模块提供了 -t 选项,可以让您设置顶层目录,因此您可以这样做(来自 package/):

python -m unittest discover -t ..

更多细节请参见unittest文档


3
谢谢!这确实有帮助。我也从Mark Lutz的《学习Python》中的模块部分受益匪浅。我意识到我需要确保从实际调用模块的目录中正确地导入,而不是从模块所在的目录中导入。因此,现在我从project/调用我的测试文件,并且test_a中的导入语句为:'import lib.lib_a'和'import spd'。结合从更高级别目录运行unittests的简单方法,使我能够修复我的代码。 - J Jones
test.py 包含什么内容? - gsanta
在这种情况下,它将是一个可执行文件,用于运行测试(类似于 unittest.main(),设置您的 sys.path 或任何所需的内容 - 对于简单的东西来说并不是必要的。 - kai
1
“test_a”和“test_b”包含什么?它们如何导入“lib_a”和“lib_b”的内容进行测试? - Tim Skov Jacobsen
像@JJones提到的那样,Mark Lutz的《学习Python》非常好地解释了模块导入过程。强烈推荐阅读。 - NFern

6

我遇到了同样的问题,kai的回答解决了我的问题。我想用test.py的内容来补充他的回答(正如@gsanta所问)。我只在Python 2.7上测试过:

from packages.tests import test_a, test_b
import unittest

# for test_a
unittest.main(test_a, exit=False)

# for test_b
unittest.main(test_b)

那么您可以直接:
../project $ python test.py

如果test_a的结构也存在,我会点赞这个。 - rufreakde

2
在一个布局中,测试处于同级别:
/project
   /tests
   /package

一种方法是从 package 目录开始,在 -s 中仅查找 tests,并使用 -t 设置顶级目录:

../package $ python3 -m unittest discover -s ../tests -t ..


那么/tests目录下的测试脚本如何从/package导入呢?是类似于import package.foo这样的方式吗? - AntonK

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接