답변:
몇 가지 문제가 있습니다.
우선, 당신이 사용하는 방식이 mock.patch옳지 않습니다. 데코레이터로 사용될 때 주어진 함수 / 클래스 (이 경우 datetime.date.today) 는 데코 레이팅 된 함수 내에서만Mock 객체로 대체합니다 . 그래서, 단지 내 것이다 당신이 원하는 것처럼 보이지 않는 다른 기능을합니다.today()datetime.date.today
정말로 원하는 것은 다음과 같습니다.
@mock.patch('datetime.date.today')
def test():
datetime.date.today.return_value = date(2010, 1, 1)
print datetime.date.today()
불행히도 이것은 작동하지 않습니다.
>>> test()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "build/bdist.macosx-10.6-universal/egg/mock.py", line 557, in patched
File "build/bdist.macosx-10.6-universal/egg/mock.py", line 620, in __enter__
TypeError: can't set attributes of built-in/extension type 'datetime.date'
파이썬 내장 타입은 불변이기 때문에 실패합니다- 자세한 내용 은 이 답변 을 참조하십시오.
이 경우 datetime.date를 직접 서브 클래스하고 올바른 함수를 만듭니다.
import datetime
class NewDate(datetime.date):
@classmethod
def today(cls):
return cls(2010, 1, 1)
datetime.date = NewDate
그리고 지금 당신은 할 수 있습니다 :
>>> datetime.date.today()
NewDate(2010, 1, 1)
datetime인스턴스를 원래 값으로 어떻게 복원 하시겠습니까? 와 함께 deepcoppy?
patch('mymodule.datetime', Mock(today=lambda: date(2017, 11, 29)))
@patch('module_you_want_to_test.date', Mock( today=Mock(return_value=datetime.date(2017, 11, 29))))있습니다.
또 다른 옵션은 https://github.com/spulec/freezegun/ 을 사용하는 것입니다
설치하십시오 :
pip install freezegun
그리고 그것을 사용하십시오 :
from freezegun import freeze_time
@freeze_time("2012-01-01")
def test_something():
from datetime import datetime
print(datetime.now()) # 2012-01-01 00:00:00
from datetime import date
print(date.today()) # 2012-01-01
또한 다른 모듈의 메소드 호출에서 다른 날짜 및 시간 호출에 영향을줍니다.
other_module.py :
from datetime import datetime
def other_method():
print(datetime.now())
main.py :
from freezegun import freeze_time
@freeze_time("2012-01-01")
def test_something():
import other_module
other_module.other_method()
그리고 마지막으로:
$ python main.py
# 2012-01-01
가치있는 것을 위해 Mock 문서는 datetime.date.today에 대해 구체적으로 이야기하며 더미 클래스를 만들지 않고도이 작업을 수행 할 수 있습니다.
https://docs.python.org/3/library/unittest.mock-examples.html#partial-mocking
>>> from datetime import date
>>> with patch('mymodule.date') as mock_date:
... mock_date.today.return_value = date(2010, 10, 8)
... mock_date.side_effect = lambda *args, **kw: date(*args, **kw)
...
... assert mymodule.date.today() == date(2010, 10, 8)
... assert mymodule.date(2009, 6, 8) == date(2009, 6, 8)
...
from datetime import date이름 from datetime import date과 호출이 date.today()나타나는 것처럼 보입니다.
나는 이것에 대해 조금 늦게 온 것 같지만 여기서 가장 큰 문제는 datetime.date.today를 직접 패치하고 문서에 따르면 이것이 잘못되었다는 것입니다.
예를 들어 테스트 된 기능이있는 파일로 가져온 참조를 패치해야합니다.
다음과 같은 functions.py 파일이 있다고 가정 해 봅시다.
import datetime
def get_today():
return datetime.date.today()
그런 다음 테스트에서 다음과 같은 것이 있어야합니다
import datetime
import unittest
from functions import get_today
from mock import patch, Mock
class GetTodayTest(unittest.TestCase):
@patch('functions.datetime')
def test_get_today(self, datetime_mock):
datetime_mock.date.today = Mock(return_value=datetime.strptime('Jun 1 2005', '%b %d %Y'))
value = get_today()
# then assert your thing...
이것이 약간 도움이되기를 바랍니다.
NameError: name 'datetime' is not defined). 테스트 파일에서 가져 오지 않은 경우 datetime.strptime참조는 어디 에서 Mock(return_value=...)제공 datetime됩니까? 업데이트 : 괜찮습니다. 방금 datetime테스트 파일에 모듈을 가져 왔습니다 . 나는 트릭이 datetime테스트 파일에서 참조를 숨기는 방법이라고 생각했습니다 .
import datetime나 from datetime import strptime? 첫 번째 작업을 수행했다면, 조롱 datetime하고 수행해야 mocked_datetime.strptime.return_value = whatever하며, 나중에 수행해야 합니다. 테스트 된 메소드가있는 파일에서 strptime 참조를 직접 조롱해야합니다.
Mock(return_value=datetime...)작동 하도록 가져 오기가 누락되었다는 것입니다.
에 추가하려면 다니엘 G의 솔루션 :
from datetime import date
class FakeDate(date):
"A manipulable date replacement"
def __new__(cls, *args, **kwargs):
return date.__new__(date, *args, **kwargs)
인스턴스화하면 일반 datetime.date 객체를 반환하지만 변경할 수있는 클래스가 만들어집니다.
@mock.patch('datetime.date', FakeDate)
def test():
from datetime import date
FakeDate.today = classmethod(lambda cls: date(2010, 1, 1))
return date.today()
test() # datetime.date(2010, 1, 1)
date/ datetime자체를 가져 오지는 않지만 전역 적으로 사용 가능한 변수를 사용하므로 아무런 문제가 없습니다 : dpaste.com/790310
나는 며칠 전에 같은 상황에 처해 있었고 내 솔루션은 모듈에서 함수를 정의하여 테스트하고 조롱하는 것이 었습니다.
def get_date_now():
return datetime.datetime.now()
오늘 저는 FreezeGun 에 대해 알아 냈습니다 .이 사건을 아름답게 다루는 것 같습니다.
from freezegun import freeze_time
import datetime
import unittest
@freeze_time("2012-01-14")
def test():
assert datetime.datetime.now() == datetime.datetime(2012, 1, 14)
가장 쉬운 방법은 다음과 같습니다.
import datetime
from unittest.mock import Mock, patch
def test():
datetime_mock = Mock(wraps=datetime.datetime)
datetime_mock.now.return_value = datetime.datetime(1999, 1, 1)
with patch('datetime.datetime', new=datetime_mock):
assert datetime.datetime.now() == datetime.datetime(1999, 1, 1)
이 솔루션에 대한주의 : 모든 기능 datetime module로부터 target_module중지됩니다 작업.
datetime_mock.now = Mock(return_value=datetime(1999, 1, 1)이로 단축 될 수도 있습니다 datetime_mock.now.return_value = datetime(1999, 1, 1). 대신에 패치를 시작하는 start()사용하는 것이 좋습니다 with patch(...):있는지 확인 컨텍스트 관리자를 그 datetime(unmocked) 일반 동작합니다 때 다시 테스트 끝.
datetime.datetime.now()조롱 하는 방법을 의미합니다 ^^?
datetime module으로부터 target_module작동이 중지됩니다.
Daniel G 솔루션을 기반으로 다음 접근 방식을 사용할 수 있습니다. 이것은로 타입 검사를 중단하지 않는 이점이 isinstance(d, datetime.date)있습니다.
import mock
def fixed_today(today):
from datetime import date
class FakeDateType(type):
def __instancecheck__(self, instance):
return isinstance(instance, date)
class FakeDate(date):
__metaclass__ = FakeDateType
def __new__(cls, *args, **kwargs):
return date.__new__(date, *args, **kwargs)
@staticmethod
def today():
return today
return mock.patch("datetime.date", FakeDate)
기본적으로 C 기반 datetime.date클래스를 자체 Python 하위 클래스로 바꾸면 원래 datetime.date인스턴스 를 생성 하고 isinstance()쿼리를 정확히 native로 응답합니다 datetime.date.
테스트에서 컨텍스트 관리자로 사용하십시오.
with fixed_today(datetime.date(2013, 11, 22)):
# run the code under test
# note, that these type checks will not break when patch is active:
assert isinstance(datetime.date.today(), datetime.date)
datetime.datetime.now()기능 을 조롱하기 위해 비슷한 접근법을 사용할 수 있습니다 .
__instancecheck__방법 으로 최대 재귀 깊이 RuntimeError가 발생합니다 .
일반적으로, 어딘가에 모듈로 가져 왔 datetime거나 아마도 datetime.date가져 왔습니다. 메소드를 모의하는보다 효과적인 방법은 메소드를 가져 오는 모듈에 패치하는 것입니다. 예:
a.py
from datetime import date
def my_method():
return date.today()
그런 다음 테스트를 위해 모의 객체 자체가 테스트 메소드의 인수로 전달됩니다. 원하는 결과 값으로 모의를 설정 한 다음 테스트중인 메소드를 호출하십시오. 그런 다음 메소드가 원하는 것을 수행했다고 주장합니다.
>>> import mock
>>> import a
>>> @mock.patch('a.date')
... def test_my_method(date_mock):
... date_mock.today.return_value = mock.sentinel.today
... result = a.my_method()
... print result
... date_mock.today.assert_called_once_with()
... assert mock.sentinel.today == result
...
>>> test_my_method()
sentinel.today
경고의 말. 조롱으로 선외로 나가는 것이 가장 확실합니다. 그렇게하면 테스트가 길어지고 이해하기 어려워지고 유지 관리가 불가능 해집니다. 당신은 간단 같은 방법을 조롱하기 전에 datetime.date.today당신이 정말로 경우 자신에게 물어, 필요 를 조롱. 테스트가 짧고 요점에 도달하고 함수를 조롱하지 않고 제대로 작동하는 경우 조롱해야 할 객체가 아니라 테스트하는 코드의 내부 세부 정보를 볼 수 있습니다.
모의 객체가 원래 모듈 을 감싸도록 구성되어 datetime.date.today()있으므로 나머지 datetime기능이 계속 작동 한다는 추가 보너스 로 조롱 하는 또 다른 방법 이 있습니다 datetime.
from unittest import mock, TestCase
import foo_module
class FooTest(TestCase):
@mock.patch(f'{foo_module.__name__}.datetime', wraps=datetime)
def test_something(self, mock_datetime):
# mock only datetime.date.today()
mock_datetime.date.today.return_value = datetime.date(2019, 3, 15)
# other calls to datetime functions will be forwarded to original datetime
노트 wraps=datetime에 인수 mock.patch()(가) - foo_module사용 기타 datetime이외의 기능을 date.today()그들이 원래의 포장에 전달됩니다 datetime모듈.
몇 가지 솔루션은 http://blog.xelnor.net/python-mocking-datetime/ 에서 설명합니다. . 요약해서 말하자면:
모의 객체 -간단하고 효율적이지만 isinstance () 검사를 중단합니다.
target = datetime.datetime(2009, 1, 1)
with mock.patch.object(datetime, 'datetime', mock.Mock(wraps=datetime.datetime)) as patched:
patched.now.return_value = target
print(datetime.datetime.now())
모의 수업
import datetime
import mock
real_datetime_class = datetime.datetime
def mock_datetime_now(target, dt):
class DatetimeSubclassMeta(type):
@classmethod
def __instancecheck__(mcs, obj):
return isinstance(obj, real_datetime_class)
class BaseMockedDatetime(real_datetime_class):
@classmethod
def now(cls, tz=None):
return target.replace(tzinfo=tz)
@classmethod
def utcnow(cls):
return target
# Python2 & Python3 compatible metaclass
MockedDatetime = DatetimeSubclassMeta('datetime', (BaseMockedDatetime,), {})
return mock.patch.object(dt, 'datetime', MockedDatetime)
로 사용:
with mock_datetime_now(target, datetime):
....
필요한 경우 패치 할 자체 "today ()"메소드를 사용할 수 있습니다. utcnow () 조롱 예제는 여기에서 찾을 수 있습니다 : https://bitbucket.org/k_bx/blog/src/tip/source/en_posts/2012-07-13-double-call-hack.rst?at=default
사용자 정의 데코레이터를 사용하여 @ user3016183 메소드를 구현했습니다.
def changeNow(func, newNow = datetime(2015, 11, 23, 12, 00, 00)):
"""decorator used to change datetime.datetime.now() in the tested function."""
def retfunc(self):
with mock.patch('mymodule.datetime') as mock_date:
mock_date.now.return_value = newNow
mock_date.side_effect = lambda *args, **kw: datetime(*args, **kw)
func(self)
return retfunc
언젠가 누군가에게 도움이 될 줄 알았는데 ...
datetime추가하지 않고 모듈 에서 함수를 조롱 할 수 있습니다side_effects
import mock
from datetime import datetime
from where_datetime_used import do
initial_date = datetime.strptime('2018-09-27', "%Y-%m-%d")
with mock.patch('where_datetime_used.datetime') as mocked_dt:
mocked_dt.now.return_value = initial_date
do()
mocker와 함께 pytest를 사용하는 사람들을 위해 여기에 내가 조롱 datetime.datetime.now()한 방법 이 있습니다. 원래 질문과 매우 유사합니다.
test_get_now(mocker):
datetime_mock = mocker.patch("blackline_accounts_import.datetime",)
datetime_mock.datetime.now.return_value=datetime.datetime(2019,3,11,6,2,0,0)
now == function_being_tested() # run function
assert now == datetime.datetime(2019,3,11,6,2,0,0)
기본적으로 모의는 지정된 날짜를 반환하도록 설정되어야합니다. 날짜 / 시간 객체를 직접 패치 할 수 없습니다.
datetime이것을 사용하여 조롱 할 수 있습니다 .
모듈에서 sources.py:
import datetime
class ShowTime:
def current_date():
return datetime.date.today().strftime('%Y-%m-%d')
당신의 tests.py:
from unittest import TestCase, mock
import datetime
class TestShowTime(TestCase):
def setUp(self) -> None:
self.st = sources.ShowTime()
super().setUp()
@mock.patch('sources.datetime.date')
def test_current_date(self, date_mock):
date_mock.today.return_value = datetime.datetime(year=2019, month=10, day=1)
current_date = self.st.current_date()
self.assertEqual(current_date, '2019-10-01')
sources패치 데코레이터에는 무엇이 있습니까?
CPython은 실제로 pure-Python Lib / datetime.py 및 C- 최적화 된 Modules / _datetimemodule.c를 사용하여 datetime 모듈을 구현합니다 . C 최적화 버전은 패치 할 수 없지만 순수 Python 버전은 패치 할 수 있습니다.
Lib / datetime.py 의 순수 파이썬 구현의 맨 아래에는 다음 코드가 있습니다.
try:
from _datetime import * # <-- Import from C-optimized module.
except ImportError:
pass
이 코드는 모든 C 최적화 정의를 가져오고 모든 순수 파이썬 정의를 효과적으로 대체합니다. CPython이 다음을 수행하여 datetime 모듈의 순수 Python 구현을 사용하도록 강제 할 수 있습니다.
import datetime
import importlib
import sys
sys.modules["_datetime"] = None
importlib.reload(datetime)
를 설정함으로써 sys.modules["_datetime"] = None, 파이썬에게 C- 최적화 된 모듈을 무시하도록 지시합니다. 그런 다음 모듈을 다시로드하여 가져옵니다._datetime 가 실패 . 이제 순수 파이썬 정의는 그대로 유지되고 정상적으로 패치 될 수 있습니다.
당신이 사용하는 경우 Pytest를 다음에 위의 코드 조각을 포함 conftest.py 당신은 패치 할 수 있습니다 datetime일반적으로 개체를.
mock도서관의 문서 : voidspace.org.uk/python/mock/examples.html#partial-mocking