Mocking the time!

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:

TypeError: can’t set attributes of built-in/extension type ‘datetime.datetime’

 

The solution

There are many ways of solving this problem though. You can take a look at some of them in the following links:

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

 

JSDayES 2016

Leave a Comment

¿Necesitas una estimación?

Calcula ahora