datetime.date.today ()를 조롱하려고 시도했지만 작동하지 않습니다.


158

왜 이것이 작동하지 않는지 말해 줄 수 있습니까?

>>> import mock
>>> @mock.patch('datetime.date.today')
... def today(cls):
...  return date(2010, 1, 1)
...
>>> from datetime import date
>>> date.today()
datetime.date(2010, 12, 19)

누군가 더 나은 방법을 제안 할 수 있습니까?



답변:


124

몇 가지 문제가 있습니다.

우선, 당신이 사용하는 방식이 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)

13
좋은 해결책이지만 불행히도 산세 문제가 발생합니다.
Baczek

14
이 답변은 훌륭하지만 클래스를 만들지 않고 날짜 시간을 조롱 할 수 있습니다. stackoverflow.com/a/25652721/117268
Emil Stenström

datetime인스턴스를 원래 값으로 어떻게 복원 하시겠습니까? 와 함께 deepcoppy?
Oleg Belousov

5
훨씬 쉬운 :patch('mymodule.datetime', Mock(today=lambda: date(2017, 11, 29)))
빅터 가브로

1
더 쉽게 할 수 @patch('module_you_want_to_test.date', Mock( today=Mock(return_value=datetime.date(2017, 11, 29))))있습니다.
Jonhy Beebop

163

또 다른 옵션은 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

13
매우 유용한 라이브러리
Shaun

3
Freezegun 테스트가 느리게 실행되는 경우 python-libfaketime을 시도 할 수도 있습니다 .
Simon Weber

훌륭한 라이브러리이지만 불행히도 Google App Engine NDB / Datastore에서는 잘 작동하지 않습니다.
brandones

나는 "freezegun"이 도서관의 이름이라는 것을 좋아합니다. 저는 파이썬 개발자를 정말 좋아합니다! :-D
MikeyE

작동하지만 freezegun은 느린 것으로 보입니다. 특히 현재 시간 동안 여러 번의 호출로 복잡한 논리가있는 경우 특히 그렇습니다.
Andrey Belyak

115

가치있는 것을 위해 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)
...

2
이것은 실제로 작동하지 않았습니다. 항목을 찾는 노력에 감사합니다.
Pradyot

8
패치 기능에서 "mymodule"이란 무엇입니까?
seufagner

4
링크를 찾을 여기 "부분 비웃음"아래
레오 C 한

3
@seufagner의 인 MyModule은 다소 혼란 방식으로 설명 voidspace.org.uk/python/mock/patch.html#where-to-patch . 모듈이 사용하는 경우 모듈 from datetime import date이름 from datetime import date과 호출이 date.today()나타나는 것처럼 보입니다.
danio

1
감사. 일했다! 예 : mock.patch ( 'tests.views.datetime')을 mock_date로 사용 : mock_date.today.return_value = datetime.datetime (2016, 9, 18) mock_date.side_effect = lambda * args, ** kw : date (* args , ** kw)
Latrova

36

나는 이것에 대해 조금 늦게 온 것 같지만 여기서 가장 큰 문제는 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테스트 파일에서 참조를 숨기는 방법이라고 생각했습니다 .
imrek 2016 년

@DrunkenMaster 나는 당신이하고있는 것과 당신이 조롱하고있는 참조의 예를보아야 할 것입니다. 당신은 뭘하고 있었 import datetimefrom datetime import strptime? 첫 번째 작업을 수행했다면, 조롱 datetime하고 수행해야 mocked_datetime.strptime.return_value = whatever하며, 나중에 수행해야 합니다. 테스트 된 메소드가있는 파일에서 strptime 참조를 직접 조롱해야합니다.
iferminm

@israelord 내가 말하고자하는 것은 마지막 코드 스 니펫 (테스트 파일)에 날짜 시간 참조가 Mock(return_value=datetime...)작동 하도록 가져 오기가 누락되었다는 것입니다.
imrek

32

에 추가하려면 다니엘 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)

2
여기서주의해야합니다. 시작 버전을 사용해야합니다. 그렇지 않으면 datetime.date (또는 datetime 또는 기타)를 사용하면 이상이 생길 수 있습니다. IE-가짜 새 전화 자체에 도달했을 때 스택 깊이에 도달했습니다.
Danny Staple

가짜 개체가 자체 모듈 인 dpaste.com/790309 에 있으면 문제가 발생하지 않습니다 . 비록 모의 함수와 같은 모듈에 있더라도 가져 오기 date/ datetime자체를 가져 오지는 않지만 전역 적으로 사용 가능한 변수를 사용하므로 아무런 문제가 없습니다 : dpaste.com/790310
eternicode

덜 간단한 설명은 여기에서 찾을 수 있습니다 : williamjohnbert.com/2011/07/…
ezdazuzena

9

나는 며칠 전에 같은 상황에 처해 있었고 내 솔루션은 모듈에서 함수를 정의하여 테스트하고 조롱하는 것이 었습니다.

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)

9

가장 쉬운 방법은 다음과 같습니다.

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중지됩니다 작업.


1
이것은 정말 좋고 간결합니다. 줄 datetime_mock.now = Mock(return_value=datetime(1999, 1, 1)이로 단축 될 수도 있습니다 datetime_mock.now.return_value = datetime(1999, 1, 1). 대신에 패치를 시작하는 start()사용하는 것이 좋습니다 with patch(...):있는지 확인 컨텍스트 관리자를 그 datetime(unmocked) 일반 동작합니다 때 다시 테스트 끝.
Dirk

내장 라이브러리를 사용하는 솔루션을 항상 선호합니다
Nam G VU

@ frx08이 조롱을 재설정하는 방법을 알고 있습니까? 나는 datetime.datetime.now()조롱 하는 방법을 의미합니다 ^^?
Nam G VU

그럼이 모의를 사용하려고 후 -이 솔루션에 대한 하나 개주의의 모든 기능이다 datetime module으로부터 target_module작동이 중지됩니다.
Nam G VU

1
@ frx08에 동의하면 with ()가 미묘한 영향을 미칩니다. 그 블록 안에 날짜가 모두 있지만 timedelta는 작동을 멈출 것입니다. 우리가 지금 조롱해야하지만 데이트 수학이 계속 진행된다면 어떨까요? 죄송합니다. 전체 datetime 모듈이 아닌 .now ()를 조롱해야합니다.
Nam G VU

7

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()기능 을 조롱하기 위해 비슷한 접근법을 사용할 수 있습니다 .


이것이 파이썬 2.7에서 작동하는지 확실하지 않습니다. 이 __instancecheck__방법 으로 최대 재귀 깊이 RuntimeError가 발생합니다 .
Dan Loewenherz

이것은 실제로 Python 2.7에서 작동하며 인스턴스 유형 확인 문제를 해결했습니다. 감사합니다!
Karatheodory

4

일반적으로, 어딘가에 모듈로 가져 왔 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당신이 정말로 경우 자신에게 물어, 필요 를 조롱. 테스트가 짧고 요점에 도달하고 함수를 조롱하지 않고 제대로 작동하는 경우 조롱해야 할 객체가 아니라 테스트하는 코드의 내부 세부 정보를 볼 수 있습니다.


2

모의 객체가 원래 모듈 을 감싸도록 구성되어 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모듈.


1
정답, 날짜를 모의해야하는 대부분의 테스트는 날짜 시간 모듈을 사용해야합니다.
Antoine Vo

1

몇 가지 솔루션은 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):
   ....


0

사용자 정의 데코레이터를 사용하여 @ 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

언젠가 누군가에게 도움이 될 줄 알았는데 ...


0

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()

0

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)

기본적으로 모의는 지정된 날짜를 반환하도록 설정되어야합니다. 날짜 / 시간 객체를 직접 패치 할 수 없습니다.


0

나는 가져 와서이 일을 만들어 datetime으로 realdatetime내가 진짜 방법과 모의에 필요한 방법을 대체 :

import datetime as realdatetime

@mock.patch('datetime')
def test_method(self, mock_datetime):
    mock_datetime.today = realdatetime.today
    mock_datetime.now.return_value = realdatetime.datetime(2019, 8, 23, 14, 34, 8, 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패치 데코레이터에는 무엇이 있습니까?
엘레나

친애하는 @elena, 거의 1 년 전에 내가 생각했던 것을 기억하기가 다소 어렵습니다)). 나는 우리의 응용 프로그램 소스의 모든 모듈, 즉 응용 프로그램의 코드를 의미한다고 생각합니다.
MTMobile

0

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일반적으로 개체를.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.