Как насмехаться с относительными путями патчей?
У меня есть что-то подобное в тестовом файле 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
.
Любые указатели?
Ответы
Ответ 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
Используйте полный путь для импорта. Например, если у вас есть эта файловая система:
Вы можете импортировать 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
.