如何让导入与 python 包和测试包一起使用

How to get imports to work with python package and testing package

我正在努力让导入为我的 python 项目工作。我创建了一个测试项目来说明我的问题。这是 Python 3.

我的目录结构如下:

project/
  test.sh
  packageA/
     __init__.py
     moduleA.py
     test/
        __init__.py
        test_moduleA.py

test.sh的内容是python3 packageA/test/test_moduleA.py

两个__init__py都是空的。

这是moduleA.py:

class A:
  def doSomething(self):
    print("Did something")

这是“test_moduleA.py”:

import unittest
from packageA.moduleA import A

class TestModuleA(unittest.TestCase):
  def testSomething(self):
    a = A()
    self.assertTrue(a.doSomething() == "Did Something")

if __name__ == '__main__':
    unittest.main()

当我 运行 test.sh 这是我得到的错误:

[project] $ ./test.sh 
Traceback (most recent call last):
  File "packageA/test/test_moduleA.py", line 2, in <module>
    from packageA.moduleA import A
ModuleNotFoundError: No module named 'packageA'
[project] $ 

我试过在“test_moduleA.py”中使用相对导入,如下所示:

from ..moduleA import A

在这种情况下,我得到的错误如下所示:

[project] $ ./test.sh 
Traceback (most recent call last):
  File "packageA/test/test_moduleA.py", line 2, in <module>
    from ..moduleA import A
ValueError: attempted relative import beyond top-level package
[project] $

如何让它正常工作? Pythonic 方法是什么?

将 shell 脚本用于 test/deploy python 并不是一个好主意,因此我建议您将 test.sh 替换为 test.py,其内容为:

import unittest

testmodules = [
    'packageA.test.test_moduleA',
]

suite = unittest.TestSuite()

for t in testmodules:
    try:
        mod = __import__(t, globals(), locals(), ['suite'])
        suitefn = getattr(mod, 'suite')
        suite.addTest(suitefn())
    except (ImportError, AttributeError):
        suite.addTest(unittest.defaultTestLoader.loadTestsFromName(t))

unittest.TextTestRunner().run(suite)

向其中添加新测试非常容易,您只需要修改测试模块列表即可。

这是我基于 this answer (see also here) and this article 建议的解决方案。

首先,将测试目录移动到项目的根目录中:

project/
  test.sh
  packageA/
     __init__.py
     moduleA.py
  test/
     __init__.py
     test_moduleA.py

然后,将 test_moduleA.py 更改如下(注意导入):

import unittest
from packageA.moduleA import A


class TestModuleA(unittest.TestCase):
    def testSomething(self):
        a = A()
        self.assertTrue(a.do_something() == "Something was done")

test.sh是这个:

#!/bin/bash
TEST_PACKAGE="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"

cd ${TEST_PACKAGE}
python3 -m unittest test.test_moduleA

为了通过测试,我也更改了 class:

class A:
    def do_something(self):
        print("do_somwthing was called")
        return "Something was done"

现在,运行 test.sh 它应该按预期工作:

$ ./test.sh 
do_somwthing was called
.
----------------------------------------------------------------------
Ran 1 test in 0.000s

OK