I need to test functions which uses datetime.datetime.now()
. What is the easiest way to do this?
9 Answers
You need to monkeypatch datetime.now function. In example below, I'm creating fixture which I can re-use later in other tests:
import datetime
import pytest
FAKE_TIME = datetime.datetime(2020, 12, 25, 17, 5, 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

- 3,946
- 2
- 33
- 41
-
11Is it possible to only replace the `now` method? – satoru Sep 26 '14 at 03:14
-
11This works, but not if you do "from datetime import datetime". Take a look here for detais: http://stackoverflow.com/questions/35431476/why-pythons-monkeypatch-doesnt-work-when-importing-a-class-instead-of-a-module – rgargente Feb 16 '16 at 11:59
-
1I was able to adjust this answer and patch `utcnow` – Dror Jan 15 '20 at 13:09
-
4Your `mydatetime` class should subclass `datetime.datetime`, otherwise it won't be able to perform any of the other methods datetime.datetime normally performs. – mareoraft Aug 31 '21 at 16:23
-
@mareoraft That might not be enough. I tried that, and it works well enough in the MCVE, but in my real environment a pandas import failed with `TypeError: type 'pandas._libs.tslibs.base.ABCTimestamp' is not dynamically allocated but its base type 'mydatetime' is dynamically allocated`. – gerrit Sep 30 '22 at 15:37
There is freezegun
module:
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()
could also be used as a context manager. The module support specifying the local timezone UTC offset.

- 399,953
- 195
- 994
- 1,670
How about using MagicMock(wrap=datetime.datetime)
?
This aproach mocks datetime.datetime.now()
but the other methods are available same with the original datetime.datetime
.
from unittest.mock import MagicMock
def test_datetime_now(monkeypatch):
import datetime
FAKE_NOW = datetime.datetime(2020, 3, 11, 14, 0, 0)
datetime_mock = MagicMock(wraps=datetime.datetime)
datetime_mock.now.return_value = FAKE_NOW
monkeypatch.setattr(datetime, "datetime", datetime_mock)
assert datetime.datetime.now() == FAKE_NOW
# the other methods are available
assert datetime.datetime.fromisoformat("2020-03-01T00:00:00") == datetime.datetime(2020, 3, 1, 0, 0, 0)
Using @pytest.fixture
approach is here.
import datetime
from unittest.mock import MagicMock
import pytest
FAKE_NOW = datetime.datetime(2020, 3, 11, 14, 0, 0)
@pytest.fixture()
def mock_datetime_now(monkeypatch):
datetime_mock = MagicMock(wraps=datetime.datetime)
datetime_mock.now.return_value = FAKE_NOW
monkeypatch.setattr(datetime, "datetime", datetime_mock)
def test_datetime_now2(mock_datetime_now):
assert datetime.datetime.now() == FAKE_NOW
assert datetime.datetime.fromisoformat("2020-03-01T00:00:00") == datetime.datetime(2020, 3, 1, 0, 0, 0)

- 163
- 2
- 10
-
-
-
1This will break code that uses `isinstance(value, datetime.datetime)`, for example, tortoise-orm. – Eugene Morozov Nov 24 '20 at 04:56
This is the fixture I use for overriding now() but keeping the rest of datetime working (RE: satoru's question).
It is not extensively tested, but it does get around issues where datetime is used in other contexts. For me this was important to keep the Django ORM working with these datetime values (Specifically 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
Perhaps off topic, but might come in handy to other people arriving at this question. This fixture allows "freezing" time, and then moving it back and forth at will within your tests:
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

- 2,678
- 25
- 24
If you are a lover of pytest-mock, then mock the datetime.now
using this.
from datetime import datetime, timezone
def test_time_now(mocker):
#patch the the datetime method in your file
mock_date = mocker.patch("your_package.your_package.datetime")
FAKE_NOW = datetime(2020, 3, 11, 14, 0, 0, tzinfo=timezone.utc)
mock_date.now.return_value = FAKE_NOW
from your_package.your_package import time_to_test
timestamp_method = time_to_test()
assert timestamp_method == FAKE_NOW

- 59
- 1
- 3
it handly to use unittest.mock.patch as context manager, wright in test:
import datetime
import pytest
from unittest.mock import patch
def test_schedule(schedules_db_client):
faked_now = datetime.datetime(2022, 1, 1, 14, 0, 0)
with patch("datetime.datetime") as mock_datetime:
mock_datetime.now.return_value = faked_now
assert schedules_db_client.get_current_attendant()

- 1,591
- 15
- 20
Adapted from the other answers:
import datetime as dt
from contextlib import contextmanager
from unittest.mock import patch
@contextmanager
def mocked_now(now):
class MockedDatetime(dt.datetime):
@classmethod
def now(cls):
return now
with patch("datetime.datetime", MockedDatetime):
yield
Used like:
def test_now():
with mocked_now(dt.datetime(2017, 10, 21)):
assert dt.datetime.now() == dt.datetime(2017, 10, 21)

- 1,103
- 2
- 14
- 35

- 120
- 1
- 9
-
4
-
You have also missed to import contextmanager: `from contextlib import contextmanager` – Fred Mar 15 '21 at 02:14
#mymodule.py
import datetime
#test file
@patch('mymodule.datetime')
def test_stomething(datetime_mock):
frozen_now = datetime.datetime(2000, 1, 1)
# I here I mock the now call
datetime_mock.datetime.now.return_value = frozen_now
# here I want timedelta to actually call the original function
datetime_mock.timedelta = datetime.timedelta

- 21
- 6
Yet another recipe, using pytest-mock
with an explicit MagicMock
to wrap only the now
method:
from datetime import datetime
from my_module import fn_returning_datetime_now
def test_datetime(mocker):
NOW = dt.datetime(1999,1,1)
mock_datetime = mocker.MagicMock(wraps=datetime)
mock_datetime.now.return_value = NOW
mocker.patch("my_module.datetime", mock_datetime)
assert fn_returning_datetime_now == NOW
This is just a variation on other answers here, using mocker.patch
instead of monkeypatch
.

- 5,305
- 1
- 6
- 26