Reputation: 6978
In some of my tests I am having a problem that they fail on Travis because of time and time zone problems, so I want to mock system time for my test. How can I do this?
Upvotes: 35
Views: 38320
Reputation: 71
You can mock built-in modules used within a specific module in your application.
For example, consider this module:
from datetime import datetime
current_timestamp = datetime.now()
To mock the datetime module in your tests, you can do the following:
import pytest
from datetime import datetime, timedelta
def test_code(mocker):
mock_datetime = mocker.patch("{path_to_code_file}.code.datetime")
mock_datetime.now.return_value = datetime.now() + timedelta(days=1)
Here, {path_to_code_file}
should be replaced with the actual import path of the code.py
file.
Upvotes: 3
Reputation: 1783
Dependency injection is another way to do it - just passing in something that gives you the time (e.g. a function).
Here are some example tests:
import datetime
from greeter import greet
def test_it_returns_good_morning_before_12():
def get_time():
return datetime.datetime(2024, 12, 8, hour=5, minute=0, second=0)
greeting = greet(get_time)
assert greeting == "Good morning!"
def test_it_returns_good_afternoon_after_12():
def get_time():
return datetime.datetime(2024, 12, 8, hour=14, minute=0, second=0)
greeting = greet(get_time)
assert greeting == "Good afternoon!"
The greet
function then calls the function it is passed to obtain the time:
def greet(get_time):
hour = get_time().hour
if hour < 12:
return "Good morning!"
else:
return "Good afternoon!"
In the production code where I need to use greet
, I'd simply pass the function that provides the system time:
greeting = greet(datetime.datetime.now)
I did write a tutorial about this over on python-forum: https://python-forum.io/thread-38222.html. That does include a link to a repo on GitHub with a more complete example (https://github.com/ndc85430/controlling-date-and-time-in-tests).
Upvotes: 0
Reputation: 3917
Similarly to freezgun
(see @Jason R. Coombs answer) you can use time-machine which should be faster.
import datetime as dt
import time_machine
@time_machine.travel("2024-01-01")
def test_date():
assert dt.datetime.now() == dt.datetime(2024, 1, 1)
For more examples check time-machine.
Upvotes: 0
Reputation: 4291
I prefer to use this code.
from unittest.mock import MagicMock
@pytest.fixture
def mocking_datetime_now(monkeypatch):
datetime_mock = MagicMock(wrap=datetime.datetime)
datetime_mock.now.return_value = datetime.datetime(2020, 3, 11, 0, 0, 0)
monkeypatch.setattr(datetime, "datetime", datetime_mock)
@pytest.fixture
def setup_db(company, company_user, mocking_datetime_now):
assert datetime.datetime.now() == datetime.datetime(2020, 3, 11, 0, 0, 0)
Upvotes: 4
Reputation: 42679
@Brian-Kruger's answer is the best one. I've voted to undelete it. In the meantime...
From the README:
from freezegun import freeze_time
@freeze_time("2012-01-14")
def test():
assert datetime.datetime.now() == datetime.datetime(2012, 1, 14)
Upvotes: 48
Reputation: 15255
AFAIK, you can't mock builtin methods.
One approach I have often done is to change my code a bit to not use datetime
directly to obtain the date, but a wrapper function somewhere:
# mymodule.py
def get_today():
return datetime.date.today()
This makes it trivial to just mock
it in your test:
def test_something():
with mock.patch('mymodule.get_today', return_value=datetime.date(2014, 6, 2)):
...
You can also use the freezegun module.
Upvotes: 29
Reputation: 4213
There are two ways you can accomplish that:
Create function which you will call instead of datetime.datetime.now()
as suggested by Bruno, but here is different implementation:
import os
import datetime
def mytoday():
if 'MYDATE' in os.environ:
return datetime.datetime.strptime(os.getenv('MYDATE'), '%m-%d-%Y').date()
else:
return datetime.date.today()
Then, in your test, you just monkeypatch environment variable:
import datetime
def test_patched_date(monkeypatch):
monkeytest.setenv('MYDATE', '05-31-2014')
assert datetime.date.today() == datetime.date(2014, 5, 31)
Monkeypatch the datetime
function:
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
Upvotes: 19