避免Python Unittest中的程序包名称冲突

时间:2018-09-28 20:45:53

标签: python unit-testing import python-import python-unittest

这是我的项目布局:

project
+-- package_1
|   +-- __init__.py
|   +-- module_1.py tests
+-- package_2
|   +-- __init__.py
|   +-- module_2.py tests
+-- tests
    +-- package_1
    |   +-- __init__.py
    |   +-- test_module_1.py
    +-- package_2
        +-- __init__.py
        +-- test_module_2.py

test_module_1.py开头为:

import package_1.module_1

test_module_2.py始于:

import package_2.module_2

从项目目录运行python -m unittest discover tests会出现错误:

EE
======================================================================
ERROR: package_1.test_module_1 (unittest.loader._FailedTest)
----------------------------------------------------------------------
ImportError: Failed to import test module: package_1.test_module_1
Traceback (most recent call last):
  File "/usr/local/Cellar/python/3.7.0/Frameworks/Python.framework/Versions/3.7/lib/python3.7/unittest/loader.py", line 434, in _find_test_path
    module = self._get_module_from_name(name)
  File "/usr/local/Cellar/python/3.7.0/Frameworks/Python.framework/Versions/3.7/lib/python3.7/unittest/loader.py", line 375, in _get_module_from_name
    __import__(name)
  File "/Users/maggyero/project/tests/package_1/test_module_1.py", line 1, in <module>
    import package_1.module_1
ModuleNotFoundError: No module named 'package_1.module_1'


======================================================================
ERROR: package_2.test_module_2 (unittest.loader._FailedTest)
----------------------------------------------------------------------
ImportError: Failed to import test module: package_2.test_module_2
Traceback (most recent call last):
  File "/usr/local/Cellar/python/3.7.0/Frameworks/Python.framework/Versions/3.7/lib/python3.7/unittest/loader.py", line 434, in _find_test_path
    module = self._get_module_from_name(name)
  File "/usr/local/Cellar/python/3.7.0/Frameworks/Python.framework/Versions/3.7/lib/python3.7/unittest/loader.py", line 375, in _get_module_from_name
    __import__(name)
  File "/Users/maggyero/project/tests/package_2/test_module_2.py", line 1, in <module>
    import package_2.module_2
ModuleNotFoundError: No module named 'package_2.module_2'


----------------------------------------------------------------------
Ran 2 tests in 0.000s

FAILED (errors=2)

在test_module_1.py的开头添加import sys; print(sys.modules['package_1']),在test_module_2.py的开头添加import sys; print(sys.modules['package_2']),以查看sys.modules cache中的内容,表明package_1和package_2在测试发现期间,已经从tests目录中导入了该文件:

<module 'package_1' from '/Users/maggyero/project/tests/package_1/__init__.py'>
<module 'package_2' from '/Users/maggyero/project/tests/package_2/__init__.py'>

即使先前已更新sys.modules,导入先前导入的程序包也会重用sys.path中的相同缓存的程序包。因此,当执行import package_1.module_1import package_2.module_2时,将重新导入测试目录(包含test_module_1和test_module_2)中的package_1和package_2,而不是项目目录(包含module_1和module_2)中的package_1和package_2,然后导入module_1和module_2,并引发ModuleNotFoundError

是否有一种解决方法,除了重命名之外,还可以避免来自tests目录的软件包遮盖了项目目录的软件包?

更新(发布答案)

下面的Laurent Laporte的另一种解决方案(他避免执行'package_1''package_2'时在sys.modules中已经有import package_1.module_1import package_2.module_2,通过使{{ 1}}和'tests.package_1'相反,这要归功于顶层目录的更改),是更新'tests.package_2'并将包重新加载到test_module_1.py中:

sys.path

和test_module_2.py:

import importlib
import pathlib
import sys
sys.path.insert(0, pathlib.Path(__file__).resolve().parents[2])

import package_1
importlib.reload(package_1)
import package_1.module_1

此解决方案的唯一优点是,tests目录不必是常规软件包(具有__init__.py文件)。因此,当Unittest允许递归命名空间包发现时(此时票证仍处于打开状态:https://bugs.python.org/issue23882)将没有任何优势。

最好使用

Laurent Laporte的解决方案,因为与包装重新装载相比,包装限定更好地区分了具有相同名称的包装。另一个很好的解决方案是包重命名(例如将package_1和package_2从tests目录重命名为test_package_1和test_package_2)。

1 个答案:

答案 0 :(得分:1)

您可以使用标志 -t -top-level-directory directory 来设置顶级目录,从而解决您的问题项目的名称(默认为开始目录)

例如:

python -m unittest discover tests -t .

但是,为了使 discover 导入您的测试模块,您需要通过在其中插入tests来将__init__.py目录变成一个包。

请参阅有关tests discovery的文档。

注释:

  • 我在PyTest上遇到了同样的问题。在GitHub上见我的study

  • 在其他开源项目中,只有一个根软件包(例如,只有package_1),没有tests/package_1目录,只有tests和所有{ {1}}个模块(可能还有子软件包)。因此,问题没有出现。