답변:
몇 가지 문제가 있습니다.
우선, 당신이 사용하는 방식이 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