#python-3.x #unit-testing #python-import #python-unittest
Вопрос:
Я издеваюсь над следующей функцией в модуле sample.py
, следуя примеру из unittest docs:
import datetime
from random import randint
def useless_date():
today_date = datetime.date.today()
x = randint(0,1000)
dt = datetime.timedelta(days = x)
return today_date dt
Я настроил файл test_file и заметил некоторое странное поведение в python, которое я не понимаю. Тесты выполняются идеально, если используются явные date
и timedelta
импортные:
from unittest import mock
from sample import useless_date
from datetime import date, timedelta
@mock.patch("sample.datetime.date")
@mock.patch("sample.datetime.timedelta")
def test_useless_date(mock_timedelta, mock_date):
mock_date.today.return_value = date(1999, 1, 1)
mock_timedelta.return_value = timedelta(days=1)
mock_date.side_effect = lambda *args, **kwargs: date(*args, **kwargs)
assert useless_date() == date(1999, 1, 2)
Однако, если я просто импортирую datetime
модуль без явного импорта date
, и timedelta
я получу ошибку рекурсии.
Код:
from unittest import mock
from sample import useless_date
import datetime
@mock.patch("sample.datetime.date")
@mock.patch("sample.datetime.timedelta")
def test_useless_date(mock_timedelta, mock_date):
date = datetime.date
timedelta = datetime.timedelta
mock_date.today.return_value = date(1999, 1, 1)
mock_timedelta.return_value = timedelta(days=1)
mock_date.side_effect = lambda *args, **kwargs: date(*args, **kwargs)
assert useless_date() == date(1999, 1, 2)
Ошибка:
__________________________________________________________________________________________________________ test_useless_date ___________________________________________________________________________________________________________
mock_timedelta = <MagicMock name='timedelta' id='4466974048'>, mock_date = <MagicMock name='date' id='4467058096'>
@mock.patch("sample.datetime.date")
@mock.patch("sample.datetime.timedelta")
def test_useless_date(mock_timedelta, mock_date):
date = datetime.date
timedelta = datetime.timedelta
mock_date.today.return_value = date(1999, 1, 1)
mock_timedelta.return_value = timedelta(days=1)
mock_date.side_effect = lambda *args, **kwargs: date(*args, **kwargs)
> assert useless_date() == date(1999, 1, 2)
test_sample.py:60:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.8/lib/python3.8/unittest/mock.py:1081: in __call__
return self._mock_call(*args, **kwargs)
/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.8/lib/python3.8/unittest/mock.py:1085: in _mock_call
return self._execute_mock_call(*args, **kwargs)
/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.8/lib/python3.8/unittest/mock.py:1146: in _execute_mock_call
result = effect(*args, **kwargs)
test_sample.py:58: in <lambda>
mock_date.side_effect = lambda *args, **kwargs: date(*args, **kwargs)
/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.8/lib/python3.8/unittest/mock.py:1081: in __call__
return self._mock_call(*args, **kwargs)
E RecursionError: maximum recursion depth exceeded
!!! Recursion detected (same locals amp; position)
======================================================================================================= short test summary info ========================================================================================================
FAILED test_sample.py::test_useless_date - RecursionError: maximum recursion depth exceeded
Версия Python: Python 3.8.2
Ответ №1:
Рекурсия в вашем тесте происходит потому, что при написании date = datetime.date
вы получаете не то, что думаете (исходную datetime.date
функцию), а функцию, над которой издеваются.
Как import datetime
и в тестируемой функции, используемый модуль является глобальным модулем, поэтому запись @mock.patch("sample.datetime.date")
имеет тот же эффект, что и запись @mock.patch("datetime.date")
— вы исправляете глобальную datetime.date
функцию. В вашем коде это означает , что date
это то же mock_date
самое, что и, и ваш побочный эффект такой же, как при написании
mock_date.side_effect = lambda *args, **kwargs: mock_date(*args, **kwargs)
Это, очевидно, вызывает рекурсию.
Этого не произойдет, если вы используете from datetime import date
, потому что в этом случае date
появляется новая ссылка на datetime.date
проживание в модуле, откуда она была импортирована. Таким образом, в вашей первой версии date
переменная ссылается на test_sample.date
(при условии test_sample
, что это имя тестового модуля), а не на datetime.date
. То же самое произошло бы, если бы вы использовали from datetime import date
sample.py
. В этом случае вам придется исправлять sample.date
, что не то же самое datetime.date
, и ваш второй тест также будет работать.
Комментарии:
1. Спасибо, что объяснили это!