After introducing the unit testing methodology and taking a look at how to mock them, now we are facing a particular case: how to mock datetime module
The problem
When testing functionality that uses somehow the “current” time in its implementation, we may need to mock today(), now() or utcnow().
But there’s a problem here. All datetime module methods are immutable as they are defined in C extension modules (the same way as builtins). Basically, that means we can’t replace attributes and we can not mock out this functions.
So if you try to mock any method of this module no matter how (using decorator, “with” statement…), you will eventually get a TypeError during setattr:
The solution
There are many ways of solving this problem though. You can take a look at some of them in the following links:
- http://www.voidspace.org.uk/python/mock/examples.html#partial-mocking
- http://stackoverflow.com/questions/4481954/python-trying-to-mock-datetime-date-today-but-not-working
The module we use in some projects at SG is based on this gist and has the following code:
#mock_datetime.py
import datetime
import mock
real_datetime_class = datetime.datetime
def mock_datetime(target, datetime_module):
class DatetimeSubclassMeta(type):
@classmethod
def __instancecheck__(mcs, obj):
return isinstance(obj, real_datetime_class)
class BaseMockedDatetime(real_datetime_class):
@classmethod
def now(cls, tz=None):
return target.replace(tzinfo=tz)
@classmethod
def utcnow(cls):
return target
@classmethod
def today(cls):
return target
# Python2 & Python3-compatible metaclass
MockedDatetime = DatetimeSubclassMeta('datetime', (BaseMockedDatetime,), {})
return mock.patch.object(datetime_module, 'datetime', MockedDatetime)
Even it could seem a bit complex, it’s really easy to use in our tests and even “isinstance” checks would pass positively, which is very interesting as they are performed when adding or subtracting datetimes . Let’s create a very simple (and unlikely real) test to see how to work with it:
import mock_datetime from mock_datetime
…
def test_is_new_years_eve_positive(self):
“””
Test that it’s the 25 December
“””
target = datetime.datetime(2014, 12, 31)
with mock_datetime(target, datetime):
is_new_years_eve = date_helpers.is_new_years_eve()
self.assertTrue(is_new_years_eve)
target = datetime.datetime(2014, 11, 31)
with mock_datetime(target, datetime):
is_new_years_eve = date_helpers.is_new_years_eve()
self.assertFalse(is_new_years_eve)
#date_helpers.py
#def is_new_years_eve(self):
# today = datetime.datetime.today()
# return today.month is 12 and today.day is 31
As you can see, we only need to import our mock_datetime method and use it in a “with statement” passing the target we want to have as date inside the block.
Related articles
Mocha, JavaScript unit testing
Control the code style of your team: ESLint in JavaScript