Мне нужно проверить функции, которые используют datetime.datetime.now()
. Каков самый простой способ сделать это?
Как monkeypatch python datetime.datetime.now с py.test?
Ответ 1
Вам нужна функция monkeypatch datetime.now. В приведенном ниже примере я создаю инструмент, который я могу повторно использовать позже в других тестах:
import datetime
import pytest
FAKE_TIME = datetime.datetime(2020, 12, 25, 17, 05, 55)
@pytest.fixture
def patch_datetime_now(monkeypatch):
class mydatetime:
@classmethod
def now(cls):
return FAKE_TIME
monkeypatch.setattr(datetime, 'datetime', mydatetime)
def test_patch_datetime(patch_datetime_now):
assert datetime.datetime.now() == FAKE_TIME
Ответ 2
Существует freezegun
модуль:
from datetime import datetime
from freezegun import freeze_time # $ pip install freezegun
@freeze_time("Jan 14th, 2012")
def test_nice_datetime():
assert datetime.now() == datetime(2012, 1, 14)
freeze_time()
также может использоваться в качестве менеджера контекста. Поддержка модуля, определяющая местное смещение UTC локального времени.
Ответ 3
Это инструмент, который я использую для переопределения now(), но сохраняя работу datetime (RE: satoru question).
Он не подвергается всестороннему тестированию, но он сталкивается с проблемами, когда datetime используется в других контекстах. Для меня это было важно, чтобы поддерживать ORM Django, работая с этими значениями datetime (в частности isinstance(Freeze.now(), datetime.datetime) == True
).
@pytest.fixture
def freeze(monkeypatch):
""" Now() manager patches datetime return a fixed, settable, value
(freezes time)
"""
import datetime
original = datetime.datetime
class FreezeMeta(type):
def __instancecheck__(self, instance):
if type(instance) == original or type(instance) == Freeze:
return True
class Freeze(datetime.datetime):
__metaclass__ = FreezeMeta
@classmethod
def freeze(cls, val):
cls.frozen = val
@classmethod
def now(cls):
return cls.frozen
@classmethod
def delta(cls, timedelta=None, **kwargs):
""" Moves time fwd/bwd by the delta"""
from datetime import timedelta as td
if not timedelta:
timedelta = td(**kwargs)
cls.frozen += timedelta
monkeypatch.setattr(datetime, 'datetime', Freeze)
Freeze.freeze(original.now())
return Freeze
Возможно, вне темы, но может пригодиться другим людям, которые приходят к этому вопросу. Этот прибор позволяет "замораживать" время, а затем перемещать его обратно и вперед по своему усмотрению:
def test_timesensitive(freeze):
freeze.freeze(2015, 1, 1)
foo.prepare() # Uses datetime.now() to prepare its state
freeze.delta(days=2)
# Does something that takes in consideration that 2 days have passed
# i.e. datetime.now() returns a date 2 days in the future
foo.do_something()
assert foo.result == expected_result_after_2_days
Ответ 4
Адаптировано из других ответов:
import datetime as dt
@contextmanager
def mocked_now(now):
class MockedDatetime(dt.datetime):
@classmethod
def now(cls):
return now
with patch("datetime.datetime", MockedDatetime):
yield
Используется как:
def test_now():
with mocked_now(dt.datetime(2017, 10, 21)):
assert dt.datetime.now() == dt.datetime(2017, 10, 21)