Как исправить модуль, который не был импортирован родительским пакетом __init__.py - PullRequest
1 голос
/ 03 марта 2020

Я пытаюсь протестировать созданный мной инструмент, который использует некоторые функции jMetalPy . У меня была / работает предыдущая версия, но сейчас я пытаюсь реорганизовать некоторые внешние зависимости (такие как вышеупомянутый jMetalPy).

Код проекта и структура

Вот минималистская структура моего project.

MyToolDirectory
  ¦--/MyTool
  ¦----/__init__.py
  ¦----/_jmetal
  ¦------/__init__.py
  ¦------/core
  ¦--------/quality_indicator.py
  ¦----/core
  ¦------/__init__.py
  ¦------/run_manager.py
  ¦----/tests
  ¦------/__init__.py
  ¦------/test_run_manager.py

Каталог _jmetal предназначен для удаления внешней зависимости от пакета jMetalPy - и я скопировал только необходимые мне пакеты / модули.

Минимальное содержимое run_manager.py

# MyTool\core\run_manager.py

import jmetal
# from jmetal.core.quality_indicators import HyperVolume  # old working version

class RunManager:
    def __init__(self):
      pass

    @staticmethod
    def calculate_hypervolume(front, ref_point):
        if front is None or len(front) < 1:
            return 0.
        hv = jmetal.core.quality_indicator.HyperVolume(ref_point)
        # hv = HyperVolume(ref_point)
        hypervolume = hv.compute(front)
        return hypervolume

Минимальное содержимое test_run_manager.py

# MyTool\tests\test_run_manager.py
import unittest
from unittest.mock import MagicMock, Mock, patch

from MyTool import core

class RunManagerTest(unittest.TestCase):
    def setUp(self):
        self.rm = core.RunManager()

    def test_calculate_hypervolume(self):
        ref_points = [0.0, 57.5]
        front = [None, None]
        # with patch('MyTool.core.run_manager.HyperVolume') as mock_HV:  # old working version
        with patch('MyTool.core.run_manager.jmetal.core.quality_indicator.HyperVolume') as mock_HV:
            mock_HV.return_value = MagicMock()
            res = self.rm.calculate_hypervolume(front, ref_points)
            mock_HV.assert_called_with(ref_points)
            mock_HV().compute.assert_called_with(front)

Основной вопрос

Когда я запускаю тест с кодом как есть, я получаю это сообщение об ошибке:

E           ModuleNotFoundError: No module named 'MyTool.core.run_manager.jmetal'; 'MyTool.core.run_manager' is not a package

Но когда я изменяю его на:

        with patch('MyTool.core.run_manager.jmetal.core') as mock_core:
            mock_HV = mock_core.quality_indicator.HyperVolume
            mock_HV.return_value = MagicMock()
            res = self.rm.calculate_hypervolume(front, ref_points)
            mock_HV.assert_called_with(ref_points)
            mock_HV().compute.assert_called_with(front)

... теперь тест проходит. Что дает?!

Почему я не могу (или, скорее, как могу) я хирургически исправить тот класс, который я хочу (то есть HyperVolume), не исправляя также весь подпакет? Есть ли способ обойти это? В jmetal.core может быть код, который должен работать нормально.

Это причина того, что это не работает только потому, что в jMetalPy's jmetal \ core \ __ init __. Py * нет оператора from . import quality_indicator. 1039 *? Потому что даже with patch('MyTool.core.run_manager.jmetal.core.quality_indicator) throws:

E           AttributeError: <module 'jmetal.core' from 'path\\to\\venv\\lib\\site-packages\\jmetal\\core\\__init__.py'> does not have the attribute 'quality_indicator'

Или я что-то не так делаю?

В случае, если речь идет только о добавлении этих операторов импорта, я мог бы сделайте это в моем подпакете _jmetal, но я надеялся позволить пользователю по умолчанию использовать собственную установку jMetalPy, если у него уже была такая, добавив ее в MyTool \ __ init__.py:

try:
    import jmetal
except ModuleNotFoundError:
    from . import _jmetal as jmetal

и затем заменить все экземпляры import jmetal на from MyTool import jmetal. Тем не менее, я столкнулся бы с той же проблемой снова и снова.

Я чувствую, что есть какая-то основная концепция, которую я не понимаю. Спасибо за помощь.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...