¿Cómo monopatch python datetime.datetime.now con py.test?
mocking (4)
Necesito probar funciones que usan datetime.datetime.now()
. ¿Cuál es la forma más fácil de hacer esto?
Necesitas la función monkeytime de datetime.now. En el siguiente ejemplo, estoy creando un accesorio que puedo volver a usar más tarde en otras pruebas:
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
Este es el accesorio que utilizo para anular ahora () pero manteniendo el resto del trabajo de fecha y hora (pregunta de RE: satoru).
No está ampliamente probado, pero soluciona los problemas donde se usa datetime en otros contextos. Para mí, esto era importante para mantener el ORM de Django trabajando con estos valores de fecha y hora (Específicamente es 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
Tal vez fuera del tema, pero podría ser útil para otras personas que llegan a esta pregunta. Este accesorio permite el tiempo de "congelación" y luego lo mueve hacia adelante y hacia atrás según lo desee en sus pruebas:
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
Hay freezegun
módulo 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()
también se puede usar como administrador de contexto. El soporte del módulo que especifica el desplazamiento UTC de la zona horaria local.
Adaptado de las otras respuestas:
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
Usado como:
def test_now():
with mocked_now(dt.datetime(2017, 10, 21)):
assert dt.datetime.now() == dt.datetime(2017, 10, 21)