Избегайте столкновения имен пакетов в Python Unittest - PullRequest
0 голосов
/ 28 сентября 2018

Вот мой макет проекта:

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)

Добавление import sys; print(sys.modules['package_1']) в начале test_module_1.py и import sys; print(sys.modules['package_2']) в начале test_module_2.py, чтобы увидеть, что находится в sys.modules cache показывает, что 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, сначала package_1 и package_2 из каталога тестов (которые содержат test_module_1 и test_module_2) повторно импортируются вместо package_1 и package_2 из каталога проекта (которые содержат module_1 и module_2), затем module_1 и module_2импортируются, вызывая ModuleNotFoundError.

Есть ли обходной путь, чтобы избежать того, что пакеты из каталога тестов затеняют пакеты из каталога проекта, кроме переименования?

Обновление (после ответа)

Альтернативное решение для нижеприведенного Лорана Лапорта (он избегает иметь 'package_1' и 'package_2' уже в sys.modules при выполнении import package_1.module_1 и import package_2.module_2, имея вместо этого '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

ЕдинственноеПреимущество этого решения заключается в том, что каталог tests не обязательно должен быть обычным пакетом (то есть с файлом __init__.py).Таким образом, не будет никакого преимущества, когда Unittest разрешит рекурсивное обнаружение пакета пространства имен (на данный момент билет еще открыт: https://bugs.python.org/issue23882).

Предпочтение должно отдаваться решению Лорана Лапорта, как квалификация пакета Лучше различать пакеты с одинаковыми именами, чем , перезагрузка пакета . Другим хорошим решением является переименование пакета (например, переименование package_1 и package_2 из каталога test в test_package_1 и test_package_2).

1 Ответ

0 голосов
/ 29 сентября 2018

Вы можете решить свою проблему, используя флаг -t , - каталог верхнего уровня каталог , чтобы установить каталог верхнего уровня вашего проекта.(по умолчанию запускается каталог)

Например:

python -m unittest discover tests -t .

Но для того, чтобы обнаружить для импорта тестовых модулей, вам нужно повернуть каталог testsв пакет, вставив в него __init__.py.

См. документацию о обнаружении тестов .

ПРИМЕЧАНИЯ:

  • Я сталкиваюсь с той же проблемой с PyTest.См. Мое исследование на GitHub.

  • В других проектах с открытым исходным кодом есть только один корневой пакет (например, только package_1), и нет *Каталог 1035 *, только tests со всеми модулями test_*.py (и, возможно, с подпакетами).Итак, проблема не появляется.

...