python datetime mocking py.test

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