Anthony Kong
Anthony Kong

Reputation: 40664

How to patch the now method of datetime while preserving other methods?

It is the function I want to test

from datetime import datetime, timedelta, time
def my_func():
    result_date = datetime.combine(datetime.now(), time.min) + timedelta(days=look_forward)
    ...

It is my unit test code

@patch('batch.mymodule.datetime')
def test_retrieve_data(self, mock_datetime):

   mock_datetime.now = Mock(return_value=datetime.datetime.strptime('Feb 14 2015', '%b %d %Y'))

   my_func

I think it works as intended. However the patch also mocked the other method combine. I will get this exception from my_func later on

BadValueError: Expected datetime, got <MagicMock name='datetime.combine().__add__()' id='4494328976'> 

I can fix it by adding this to the unit test:

mock_datetime.combine = datetime.datetime.combine

But it means I have to patch each individual method in datetime if it is used.

Is there any better, simpler alternative?

Upvotes: 2

Views: 566

Answers (2)

birkett
birkett

Reputation: 10101

This does not answer your question, but I just think it's worth mentioning another approach. For my project I used freezegun.

from freezegun import freeze_time
import datetime

class SomeTest(TestCase):

    @freeze_time("2015-08-10 00:00:00")
    def some_test_case(self):
        print(datetime.now())

  2015-08-10 00:00:00

Upvotes: 1

falsetru
falsetru

Reputation: 369074

Define datetime.datetime subclass to use it as a mock object's class:

class MockDatetime(datetime.datetime):
    fake_now = None

    @classmethod
    def now(cls):
        return cls.fake_now


class TestFoo(unittest.TestCase):
    @patch('mod.datetime', MockDatetime)
    def test_retrieve_data(self):
        MockDatetime.fake_now = datetime.datetime(2015, 2, 14)
        my_func()

DEMO: http://asciinema.org/a/63nrqzrysunyeq76xl72f943k

Upvotes: 3

Related Questions