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
$ 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.
Improve your Django develompent experience with my new book.
One summary email a week, no spam, I pinky promise.
- Introducing time-machine, a New Python Library for Mocking the Current Time
- How I Import Python’s datetime Module