在 Python 单元测试中避免包名冲突
Avoid package name collision in 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 显示在测试发现时已经导入了tests目录下的package_1和package_2:
<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_1
和 import 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
.
除了重命名之外,是否有解决方法可以避免测试目录中的包影响项目目录中的包?
更新(post 回答)
Laurent Laporte 下面的替代解决方案(他在执行 import package_1.module_1
和 import package_2.module_2
时避免了 'package_1'
和 'package_2'
已经在 sys.modules
中,方法是'tests.package_1'
和 'tests.package_2'
相反,由于更改了顶级目录)是更新 sys.path
并重新加载 test_module_1.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
和test_module_2.py:
import importlib
import pathlib
import sys
sys.path.insert(0, pathlib.Path(__file__).resolve().parents[2])
import package_2
importlib.reload(package_2)
import package_2.module_2
此解决方案的唯一优点是测试目录不需要是常规包(即具有 __init__.py 文件)。因此,当 Unittest 允许递归命名空间包发现时,不会有任何优势(目前票证仍然开放:https://bugs.python.org/issue23882)。
Laurent Laporte的解决方案应该是首选,因为包限定比包重新加载更能区分同名包。另一个好的解决方案是 package 重命名(例如将 package_1 和 package_2 从测试目录重命名为 test_package_1 和 test_package_2) .
你可以使用标志 -t, --top-level-directory directory 来解决你的问题 设置项目的顶层目录(默认为开始目录)
例如:
python -m unittest discover tests -t .
但是,为了让 discover 导入您的测试模块,您需要通过在其中插入 __init__.py
将 tests
目录变成一个包.
请参阅有关 tests discovery 的文档。
备注:
我用 PyTest 遇到了同样的问题。在 GitHub 上查看我的 study。
其他开源项目只有一个根包(比如只有package_1
),没有tests/package_1
目录,只有tests
所有 test_*.py
模块(可能还有子包)。所以,问题没有出现。
这是我的项目布局:
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 显示在测试发现时已经导入了tests目录下的package_1和package_2:
<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_1
和 import 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
.
除了重命名之外,是否有解决方法可以避免测试目录中的包影响项目目录中的包?
更新(post 回答)
Laurent Laporte 下面的替代解决方案(他在执行 import package_1.module_1
和 import package_2.module_2
时避免了 'package_1'
和 'package_2'
已经在 sys.modules
中,方法是'tests.package_1'
和 'tests.package_2'
相反,由于更改了顶级目录)是更新 sys.path
并重新加载 test_module_1.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
和test_module_2.py:
import importlib
import pathlib
import sys
sys.path.insert(0, pathlib.Path(__file__).resolve().parents[2])
import package_2
importlib.reload(package_2)
import package_2.module_2
此解决方案的唯一优点是测试目录不需要是常规包(即具有 __init__.py 文件)。因此,当 Unittest 允许递归命名空间包发现时,不会有任何优势(目前票证仍然开放:https://bugs.python.org/issue23882)。
Laurent Laporte的解决方案应该是首选,因为包限定比包重新加载更能区分同名包。另一个好的解决方案是 package 重命名(例如将 package_1 和 package_2 从测试目录重命名为 test_package_1 和 test_package_2) .
你可以使用标志 -t, --top-level-directory directory 来解决你的问题 设置项目的顶层目录(默认为开始目录)
例如:
python -m unittest discover tests -t .
但是,为了让 discover 导入您的测试模块,您需要通过在其中插入 __init__.py
将 tests
目录变成一个包.
请参阅有关 tests discovery 的文档。
备注:
我用 PyTest 遇到了同样的问题。在 GitHub 上查看我的 study。
其他开源项目只有一个根包(比如只有
package_1
),没有tests/package_1
目录,只有tests
所有test_*.py
模块(可能还有子包)。所以,问题没有出现。