3

I have a Django test where I need to mock datetime.now(), because the view that it tests uses datetime.now()

I'm using Michael Foord's mock library, version 1.0.1.
I'm looking for a solution without using other libraries such as freezegun.

Most examples like this and this import datetime and override it, but I'm importing datetime.datetime and im trying to override it, and for some reason this doesn't work.

Overriding datetime works:

import mock
import datetime

class FixedDate(datetime.datetime):

    @classmethod
    def now(cls):
        return cls(2010, 1, 1)

@mock.patch('datetime.datetime', FixedDate)
def myTest():
    print(datetime.datetime.now())

myTest()

But I want to import datetime.datetime and do something like this:

import mock
from datetime import datetime

class FixedDate(datetime):

    @classmethod
    def now(cls):
        return cls(2010, 1, 1)

@mock.patch('datetime', FixedDate)
def myTest():
    print(datetime.now())

myTest()

This causes the TypeError: Need a valid target to patch. You supplied: 'datetime'.

The Mock library also states:

target should be a string in the form ‘package.module.ClassName’. The target is imported and the specified object replaced with the new object, so the target must be importable from the environment you are calling patch from.

Is there any way to path only the datetime and not the datetime.datetime?

Nb. I saw also this example, but it won't work for me, cause I dont have one function that returns the datetime, but my view uses datetime.now()

Kemeia
  • 826
  • 2
  • 9
  • 24

1 Answers1

5

You should patch objects in the module you're testing, not in the module with tests.

Minimal working example based on your code:

app.py

from datetime import datetime

def my_great_func():
    # do something
    return datetime.now()

tests.py (notice how datetime in app.py is patched)

from datetime import datetime
from unittest import mock
from app import my_great_func

@mock.patch('app.datetime')
def test_my_great_func(mocked_datetime):
    mocked_datetime.now.return_value = datetime(2010, 1, 1)
    assert my_great_func() == datetime(2010, 1, 1)

Test execution results:

$ pytest -vvv tests.py 
======================= test session starts =========================
platform linux -- Python 3.5.2, pytest-3.2.1, py-1.4.34, pluggy-0.4.0
cachedir: .cache
rootdir: /home/xyz/projects/tmp, inifile:
plugins: mock-1.6.2, celery-4.1.0
collected 1 item                                                   

tests.py::test_my_great_func PASSED

==================== 1 passed in 0.00 seconds =======================
kchomski
  • 2,872
  • 20
  • 31
  • Thanks for the answer! I'm not using Python3 with the build-in unittest library, but Python2.7 with Michael Foord's mock library Following your answer I tried to import @mock.patch('app.datetime'), but this resulted in an error. – Kemeia Aug 21 '17 at 06:07
  • Actually I got it working with Python 2.7, the problem only is that you are overriding the entire datetime object, not only datetime.now, which makes using the datetime object for other purposes (like datetime(2017, 8, 22) or datetime.timedelta()) impossible – Kemeia Aug 21 '17 at 11:47