Подтвердить что ты не робот

Как насмехаться с относительными путями патчей?

У меня есть что-то подобное в тестовом файле python:

from mock import patch,
from ..monkey import ook
[...]
@patch('monkey.ook', Mock(return_value=None))
def test_run_ook (self, mock_ook):
    self.assertIsNone(ook())
    mock_ook.run.assert_called_once_with('')

Когда я запускаю этот тест, я получаю ImportError: No module named monkey. Ясно, что путь, который я исправляю, не прав. Тем не менее, я не уверен, как сделать это правильно, не возившись с sys.path или PYTHONPATH.

Любые указатели?

4b9b3361

Ответ 1

Из того, что я собираю, с макетом, вы должны указать пунктирное имя при исправлении. К счастью, каждый модуль имеет доступ к специальной переменной уровня модуля __name__, которая содержит имя модуля. Используя это, если вы хотите поменять локальные переменные на свой модуль, вы должны сделать что-то вроде следующего:

import mock
import unittest

ook = lambda: "the ook"


class OokTest(unittest.TestCase):

    def test_ook(self):
        with mock.patch(__name__ + '.ook', return_value=None):
            self.assertIsNone(ook())
        self.assertEquals(ook(), "the ook")

    # the patch decorator should work the same way, I just tend to use the
    # context manager out of personal preference
    @mock.patch(__name__ + '.ook', return_value=None)
    def test_ook_2(self, mock_ook):
        self.assertIsNone(ook())

Предполагая, что вы сохранили этот файл как quicktest.py, модульные тесты дают следующий результат:

$ python -m unittest quicktest
..
----------------------------------------------------------------------
Ran 2 tests in 0.001s

OK

И, конечно, from a.b import c дает вам простой переменный c в вашем пакете, поэтому этот же механизм должен работать.

Ответ 2

Я использовал leo-the-manic solution, пока не наткнулся на это, используя patch.object - который выглядит мне даже лучше:

from unittest.mock import patch,
from .. import monkey
[...]
@patch.object(monkey, 'ook', Mock(return_value=None))
def test_run_ook (self, mock_ook):
    self.assertIsNone(monkey.ook())
    mock_ook.run.assert_called_once_with('')

Преимущества:

  • Не требуется код шаблона __name__ + '.object_to_be_patched'
  • Все зависимости тестового примера четко указаны в начале файла в виде операторов import.

Недостатки:

  • Вы не можете сделать from ..monkey import ook, но вам нужно сделать from .. import monkey и получить доступ ook через monkey, т.е. monkey.ook. В случаях, когда мне нужно писать это часто, я добавлю ook = monkey.ook в операторы импорта для удобства.

Ответ 3

Основываясь на принятом ответе, я считаю, что это самый чистый способ достижения желаемой цели:

from mock import patch
from .. import monkey

@patch(monkey.__name__+'.ook', Mock(return_value=None))
def test_run_ook (self, mock_ook):
    self.assertIsNone(monkey.ook())
    mock_ook.run.assert_called_once_with('')

Ответ 4

Не уверен, что это лучший способ (или даже рекомендуется), но один из способов - использовать что-то вроде:

from mock import patch,
from ..monkey import ook
[...]

package_base = __package__.rsplit('.', 1)[0]

@patch('{0}.monkey.ook'.format(package_base), Mock(return_value=None))
def test_run_ook (self, mock_ook):
    self.assertIsNone(ook())
    mock_ook.run.assert_called_once_with('')

Ответ 5

Используйте полный путь для импорта. Например, если у вас есть эта файловая система:

  • корень /
    • Dumy/
      • Foo/
        • module.py
    • dummy2/
      • module2.py

Вы можете импортировать module.py из модуля2.py, используя:

from root.dummy.foo import module

Ответ 6

Я думаю, что это происходит из-за того, что вы не импортируете monkey, а вместо этого импортируете ook. Если вы импортируете обезьяну из .., тогда она должна работать. В противном случае просто назовите патч на ook.

Ответ 7

Когда вы выполняете from ..monkey import ook из модуля pkg1.pgk2.mymodule, в конце концов, с pkg1.pgk2.mymodule.ook.

Теперь ook находится в пространстве имен модуля, где вы выполнили from ... import .... И это та цель, которую вам нужно запланировать.

Итак, вы просто патчем pkg1.pkg2.mymodule.ook:

from unittest.mock import patch # mypackage.mymodule.patch
from ..monkey import ook        # mypacket.mymodule.ook

with patch("pkg1.pgk2.mymodule.ook"):
   ....

Как уже указывалось другими, если вы используете патч из того же модуля, где вы делали импорт, вы можете использовать __name__ для получения точечного имени пакета, но если вы исправляете его из другого модуля, вам нужно указать его,

Просто помните, что все, что вы импортируете, исправлено из целевого modulethatimports.nameimported.