How to Mock the Current Date and Time in Python

If you’re testing Python code that relies on the current date or time, you will probably want to mock time to test different scenarios. For example, what happens when you run a certain piece of code on February 29? (A common source of bugs.)
Unfortunately such mocking is not so easy. If you try using unittest.mock
to swap functions in the datetime
module, you’ll hit a TypeError
. For example this code:
import datetime as dt
from unittest import mock
with mock.patch.object(dt.date, "today", return_value=dt.date(2020, 2, 29)):
print(dt.date.today())
…will blow up with this TypeError
:
$ python example.py
Traceback (most recent call last):
File "/.../lib/python3.9/unittest/mock.py", line 1502, in __enter__
setattr(self.target, self.attribute, new_attr)
TypeError: can't set attributes of built-in/extension type 'datetime.date'
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/Users/chainz/tmp/example.py", line 4, in <module>
with mock.patch.object(dt.date, 'today', return_value=dt.date(2020, 2, 29)):
File "/.../lib/python3.9/unittest/mock.py", line 1515, in __enter__
if not self.__exit__(*sys.exc_info()):
File "/.../lib/python3.9/unittest/mock.py", line 1521, in __exit__
setattr(self.target, self.attribute, self.temp_original)
TypeError: can't set attributes of built-in/extension type 'datetime.date'
This is because the datetime
module is built in C, and unittest.mock
only supports modifying pure Python objects and functions. So what are the alternatives?
One option is to refactor your code to use dependency injection, as suggested in this post by Haki Benita. But this can be time consuming, changes the layout of the code purely for the tests, and can be a large refactoring to impose on an otherwise working system.
Another option is to use a library that specifically mocks the date and time. These workaround the TypeError
issue and also mock all of Python’s date and time functions at once to return consistent values.
One such library is time-machine
, which I wrote earlier this year. With it, you can easily mock the current date and time by passing the point in time you’d like to end up at:
import datetime as dt
import time_machine
with time_machine.travel(dt.date(2020, 2, 29)):
print(dt.date.today())
This works as expected:
$ python example.py
2020-02-29
time-machine
was inspired by an earlier library called freezegun
, with some improvements in terms of performance and consistency. For more information on the different ways you can use time-machine, see its README on PyPI.
Read my book Boost Your Django DX, freshly updated in November 2024.
One summary email a week, no spam, I pinky promise.
Related posts:
- Introducing time-machine, a New Python Library for Mocking the Current Time
- How I Import Python’s datetime Module
Tags: python