244

Can anyone tell me why this isn't working?

>>> import mock
>>> @mock.patch('datetime.date.today')
... def today(cls):
...  return date(2010, 1, 1)
...
>>> from datetime import date
>>> date.today()
datetime.date(2010, 12, 19)

Perhaps someone could suggest a better way?

martineau
  • 119,623
  • 25
  • 170
  • 301
Belmin Fernandez
  • 8,307
  • 9
  • 51
  • 75
  • 1
    Docs of the `mock` library: http://www.voidspace.org.uk/python/mock/examples.html#partial-mocking – guettli Jun 11 '14 at 10:43
  • 2
    [freezegun](https://github.com/spulec/freezegun) – Cabrera Jun 24 '18 at 15:52
  • 2
    In my opinion this should be the accepted answer, as it doesn't require a new dependency, and doesn't impair the functionality for the rest of the mocked module: https://stackoverflow.com/a/55187924/4960855 – EliadL Mar 20 '21 at 03:49
  • I foud this post useful for the purpose of my test (mocking datetime.now()): https://stackoverflow.com/questions/20503373/how-to-monkeypatch-pythons-datetime-datetime-now-with-py-test. A similar approach is followed also here: https://stackoverflow.com/questions/4481954/trying-to-mock-datetime-date-today-but-not-working/55187924#55187924 – kevin Mar 18 '22 at 07:15

23 Answers23

225

Another option is to use https://github.com/spulec/freezegun/

Install it:

pip install freezegun

And use it:

from freezegun import freeze_time

@freeze_time("2012-01-01")
def test_something():

    from datetime import datetime
    print(datetime.now()) #  2012-01-01 00:00:00

    from datetime import date
    print(date.today()) #  2012-01-01

It also affects other datetime calls in method calls from other modules:

other_module.py:

from datetime import datetime

def other_method():
    print(datetime.now())    

main.py:

from freezegun import freeze_time

@freeze_time("2012-01-01")
def test_something():

    import other_module
    other_module.other_method()

And finally:

$ python main.py
# 2012-01-01
Mehdi Behrooz
  • 2,904
  • 1
  • 17
  • 6
164

For what it's worth, the Mock docs talk about datetime.date.today specifically, and it's possible to do this without having to create a dummy class:

https://docs.python.org/3/library/unittest.mock-examples.html#partial-mocking

>>> from datetime import date
>>> with patch('mymodule.date') as mock_date:
...     mock_date.today.return_value = date(2010, 10, 8)
...     mock_date.side_effect = lambda *args, **kw: date(*args, **kw)
...
...     assert mymodule.date.today() == date(2010, 10, 8)
...     assert mymodule.date(2009, 6, 8) == date(2009, 6, 8)
...
grrrrrr
  • 1,395
  • 12
  • 29
kpup
  • 1,781
  • 1
  • 10
  • 9
  • 3
    This didn't really work for me. Though I appreciate the effort in locating the entry. – Pradyot Mar 27 '15 at 17:33
  • 14
    what means "mymodule" at patch function ? – seufagner Sep 02 '15 at 15:21
  • 4
    Found the link [here](http://www.voidspace.org.uk/python/mock/examples.html) under "Partial Mocking" – BrockLee Oct 14 '15 at 19:26
  • 3
    @seufagner mymodule is explained in a rather confusing fashion at http://www.voidspace.org.uk/python/mock/patch.html#where-to-patch. It seems that if your module uses `from datetime import date` then it is the name of the module where `from datetime import date` and the call to `date.today()` appears – danio Jul 19 '16 at 13:15
  • 1
    Thanks. Worked! Example: with mock.patch('tests.views.datetime') as mock_date: mock_date.today.return_value = datetime.datetime(2016, 9, 18) mock_date.side_effect = lambda *args, **kw: date(*args, **kw) – Latrova Sep 28 '16 at 19:00
  • 1
    @seufagner "mymodule " in example is module where tested function/class located. Actually function that should use mocked date – wolendranh Sep 29 '16 at 19:59
  • For brevity and DRY, you can wrap this into a new resource handler: http://stackoverflow.com/a/40334807/1464495 – brandones Nov 01 '16 at 13:25
  • 1
    Thanks. Since the guy in the question used patch as decorator I would suggest an edit to `@patch('mymodule.date', autospec=True)` Also I do not think that the side effect lambda is required in this case – Luca Ambrosini Oct 05 '18 at 09:14
  • Works for me, and indeed the side effect lambda was not necessary – Romain Sep 05 '19 at 08:45
  • Breaks comparison operators: `TypeError: '<' not supported between instances of 'datetime.datetime' and 'MagicMock'` – Midiparse Dec 03 '19 at 12:20
152

There are a few problems.

First of all, the way you're using mock.patch isn't quite right. When used as a decorator, it replaces the given function/class (in this case, datetime.date.today) with a Mock object only within the decorated function. So, only within your today() will datetime.date.today be a different function, which doesn't appear to be what you want.

What you really want seems to be more like this:

@mock.patch('datetime.date.today')
def test():
    datetime.date.today.return_value = date(2010, 1, 1)
    print datetime.date.today()

Unfortunately, this won't work:

>>> test()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "build/bdist.macosx-10.6-universal/egg/mock.py", line 557, in patched
  File "build/bdist.macosx-10.6-universal/egg/mock.py", line 620, in __enter__
TypeError: can't set attributes of built-in/extension type 'datetime.date'

This fails because Python built-in types are immutable - see this answer for more details.

In this case, I would subclass datetime.date myself and create the right function:

import datetime
class NewDate(datetime.date):
    @classmethod
    def today(cls):
        return cls(2010, 1, 1)
datetime.date = NewDate

And now you could do:

>>> datetime.date.today()
NewDate(2010, 1, 1)
Community
  • 1
  • 1
Daniel G
  • 67,224
  • 7
  • 42
  • 42
  • 14
    a nice solution, but unfortunately causes problems with pickling. – Baczek Mar 22 '11 at 09:15
  • 23
    While this answer is good, it's possible to mock datetime without creating a class: http://stackoverflow.com/a/25652721/117268 – Emil Stenström Jan 22 '15 at 15:03
  • How would you restore the `datetime` instance to it original value? with `deepcoppy` ? – Oleg Belousov Jan 10 '17 at 15:48
  • 9
    Much easier to do: `patch('mymodule.datetime', Mock(today=lambda: date(2017, 11, 29)))` – Victor Gavro Nov 29 '17 at 17:26
  • 2
    More much easier to do `@patch('module_you_want_to_test.date', Mock( today=Mock(return_value=datetime.date(2017, 11, 29))))`. – Max Jul 19 '18 at 11:54
  • No need to restore it if you do it like the solution of @eternicode https://stackoverflow.com/a/5437199/5153500 use `@mock.patch('datetime.date', NewDate)` instead of `datetime.date = NewDate` – Semih Sezer Dec 11 '18 at 01:00
56

Here's another way to mock datetime.date.today() with an added bonus that the rest of datetime functions continue to work, as the mock object is configured to wrap the original datetime module:

from unittest import mock, TestCase

import foo_module

class FooTest(TestCase):

    @mock.patch(f'{foo_module.__name__}.datetime', wraps=datetime)
    def test_something(self, mock_datetime):
        # mock only datetime.date.today()
        mock_datetime.date.today.return_value = datetime.date(2019, 3, 15)
        # other calls to datetime functions will be forwarded to original datetime

Note the wraps=datetime argument to mock.patch() – when the foo_module uses other datetime functions besides date.today() they will be forwarded to the original wrapped datetime module.

mrts
  • 16,697
  • 8
  • 89
  • 72
  • 2
    Great answer, most tests where you need to mock date you'll need to use datetime module – Antoine Vo Oct 24 '19 at 19:42
  • 5
    This should be the accepted answer. Thanks to this I now know about `patch(wraps=)` which doesn't seem to appear in official documentation. As stated, this solution also lets you keep the entire functionality for the rest of the module. – EliadL Mar 18 '21 at 23:38
  • 1
    Thank your for the bounty, @EliadL! – mrts Mar 20 '21 at 20:19
  • Works for me. NB for those not in the know (I was one), the patch fixture (if this the right term) here must be the first parameter of the test (after `self`) – mike rodent Oct 30 '21 at 12:19
  • @EliadL It **is** there in the docs, kind of: https://docs.python.org/3/library/unittest.mock.html#patch ... and look at the bottom of that para: *"patch() takes arbitrary keyword arguments. These will be passed to AsyncMock if the patched object is asynchronous, to MagicMock otherwise or to new_callable if specified."*. – mike rodent Oct 31 '21 at 08:36
43

I guess I came a little late for this but I think the main problem here is that you're patching datetime.date.today directly and, according to the documentation, this is wrong.

You should patch the reference imported in the file where the tested function is, for example.

Let's say you have a functions.py file where you have the following:

import datetime

def get_today():
    return datetime.date.today()

then, in your test, you should have something like this

import datetime
import unittest

from functions import get_today
from mock import patch, Mock

class GetTodayTest(unittest.TestCase):

    @patch('functions.datetime')
    def test_get_today(self, datetime_mock):
        datetime_mock.date.today = Mock(return_value=datetime.strptime('Jun 1 2005', '%b %d %Y'))
        value = get_today()
        # then assert your thing...

Hope this helps a little bit.

iferminm
  • 2,019
  • 19
  • 34
  • This looks very compelling, but I cannot get this to run (throws a ```NameError: name 'datetime' is not defined```). Where does the ```datetime.strptime``` reference in ```Mock(return_value=...)``` come from if you are not importing ```datetime``` in your test file? UPDATE: It's OK, I just went ahead and imported the ```datetime``` module in the test file. I thought the trick was some how that you are hiding the ```datetime``` reference from the test file. – imrek May 01 '17 at 19:27
  • @DrunkenMaster I'd have to see an example of what you were doing and which reference you were mocking. were you doing `import datetime` or `from datetime import strptime`? if you were doing the first one, you'd have to mock `datetime` and do `mocked_datetime.strptime.return_value = whatever`, is the later one, you'd have to directly mock the strptime reference in the file where the tested method lives. – iferminm May 04 '17 at 14:08
  • @israelord What I meant to say is that your last code snippet (the test file) is missing an import for the datetime reference to make `Mock(return_value=datetime...)` work. – imrek May 04 '17 at 14:14
  • However, this would not work if the OP just wanted to mock the today method leaving the rest untouched. – Javi Torre Jan 03 '22 at 23:40
34

To add to Daniel G's solution:

from datetime import date

class FakeDate(date):
    "A manipulable date replacement"
    def __new__(cls, *args, **kwargs):
        return date.__new__(date, *args, **kwargs)

This creates a class which, when instantiated, will return a normal datetime.date object, but which is also able to be changed.

@mock.patch('datetime.date', FakeDate)
def test():
    from datetime import date
    FakeDate.today = classmethod(lambda cls: date(2010, 1, 1))
    return date.today()

test() # datetime.date(2010, 1, 1)
Nam G VU
  • 33,193
  • 69
  • 233
  • 372
eternicode
  • 6,805
  • 4
  • 33
  • 39
  • 2
    Be very careful here - you must use the from version, otherwise you may get weirdness if you use datetime.date (or datetime or others). IE - stack depth reached when your fake new calls itself. – Danny Staple Aug 17 '12 at 16:41
  • You won't have that problem if the fake object is in its own module: http://dpaste.com/790309/ . Though, even if it's in the same module as the mocked function, it doesn't import `date`/`datetime` itself, it uses the globally-available variable, so there should be no problem: http://dpaste.com/790310/ – eternicode Aug 23 '12 at 00:12
  • a less brief explanation can be found here: http://www.williamjohnbert.com/2011/07/how-to-unit-testing-in-django-with-mocking-and-patching/ – ezdazuzena Aug 08 '14 at 13:44
16

The easiest way for me is doing this:

import datetime
from unittest.mock import Mock, patch

def test():
    datetime_mock = Mock(wraps=datetime.datetime)
    datetime_mock.now.return_value = datetime.datetime(1999, 1, 1)
    with patch('datetime.datetime', new=datetime_mock):
        assert datetime.datetime.now() == datetime.datetime(1999, 1, 1)

CAUTION for this solution: all functionality from datetime module from the target_module will stop working.

frx08
  • 4,222
  • 8
  • 37
  • 45
  • 1
    This is really nice and concise. The line `datetime_mock.now = Mock(return_value=datetime(1999, 1, 1)` could even be shortened to `datetime_mock.now.return_value = datetime(1999, 1, 1)`. Instead of starting the patch with `start()`, consider using the `with patch(...):` context manager to make sure that `datetime` behaves regular (unmocked) again when your test ends. – Dirk Jun 01 '18 at 16:24
  • Always favor of solution that make use of the built-in library – Nam G VU Jul 24 '18 at 08:54
  • @frx08 May I know how to reset this mocking? I mean how to get `datetime.datetime.now()` unmocked ^^? – Nam G VU Jul 24 '18 at 08:55
  • Well after trying to use this mock - one CAUTION for this solution is all functionality from `datetime module` from the `target_module` will stop working. – Nam G VU Jul 24 '18 at 15:44
  • @NamGVU as @Dirk suggested you should consider using the `with patch(...):` context manager – frx08 Jul 31 '18 at 13:26
  • 2
    Agree @frx08 the with() would subtle the pain. Though inside that block all e.g. date, timedelta will stop working. What if we need now mocked but date math still go on? Sorry, we must have .now() mocked only not the whole datetime module. – Nam G VU Jul 31 '18 at 17:48
10

I faced the same situation a couple of days ago, and my solution was to define a function in the module to test and just mock that:

def get_date_now():
    return datetime.datetime.now()

Today I found out about FreezeGun, and it seems to cover this case beautifully

from freezegun import freeze_time
import datetime
import unittest


@freeze_time("2012-01-14")
def test():
    assert datetime.datetime.now() == datetime.datetime(2012, 1, 14)
Hito_kun
  • 962
  • 3
  • 13
  • 26
8

You can use the following approach, based on Daniel G solution. This one has advantage of not breaking type checking with isinstance(d, datetime.date).

import mock

def fixed_today(today):
    from datetime import date

    class FakeDateType(type):
        def __instancecheck__(self, instance):
            return isinstance(instance, date)

    class FakeDate(date):
        __metaclass__ = FakeDateType

        def __new__(cls, *args, **kwargs):
            return date.__new__(date, *args, **kwargs)

        @staticmethod
        def today():
            return today

    return mock.patch("datetime.date", FakeDate)

Basically, we replace C-based datetime.date class with our own python subclass, that produces original datetime.date instances and responds to isinstance() queries exactly as native datetime.date.

Use it as context manager in your tests:

with fixed_today(datetime.date(2013, 11, 22)):
    # run the code under test
    # note, that these type checks will not break when patch is active:
    assert isinstance(datetime.date.today(), datetime.date)

Similar approach can be used to mock datetime.datetime.now() function.

Andrey Lebedev
  • 429
  • 3
  • 10
  • 1
    I'm not sure this works in Python 2.7. I'm getting a maximum recursion depth RuntimeError with the `__instancecheck__` method. – Dan Loewenherz Dec 12 '14 at 22:28
  • 1
    This indeed works in Python 2.7, and it solved my problem with instance type check, thanks! – Karatheodory Jul 06 '17 at 14:54
  • For those who meet the maximum recursion error: the trick is to do the import with `from datetime import date` inside the `fixed_today` function exactly as reported in the answer. Just using `import datetime` leads to the recursion error, not sure why. – Valentino Nov 19 '22 at 22:25
6

We can use pytest-mock (https://pypi.org/project/pytest-mock/) mocker object to mock datetime behaviour in a particular module

Let's say you want to mock date time in the following file

# File path - source_dir/x/a.py
import datetime

def name_function():
     name = datetime.now()
     return f"name_{name}"

In the test function, mocker will be added to the function when test runs

def test_name_function(mocker):
     mocker.patch('x.a.datetime')
     x.a.datetime.now.return_value = datetime(2019, 1, 1)

     actual = name_function()

     assert actual == "name_2019-01-01"
  • 2
    This is the best answer by far, and easily adaptable to regular `mock` or `unittest.mock` (since `pytest-mock` is just a syntax wrapper around that). Patch the C module itself and then use a series of Mock objects to get the patch you need on functions or attributes. – ely Sep 25 '20 at 14:56
5

CPython actually implements the datetime module using both a pure-Python Lib/datetime.py and a C-optimized Modules/_datetimemodule.c. The C-optimized version cannot be patched but the pure-Python version can.

At the bottom of the pure-Python implementation in Lib/datetime.py is this code:

try:
    from _datetime import *  # <-- Import from C-optimized module.
except ImportError:
    pass

This code imports all the C-optimized definitions and effectively replaces all the pure-Python definitions. We can force CPython to use the pure-Python implementation of the datetime module by doing:

import datetime
import importlib
import sys

sys.modules["_datetime"] = None
importlib.reload(datetime)

By setting sys.modules["_datetime"] = None, we tell Python to ignore the C-optimized module. Then we reload the module which causes the import from _datetime to fail. Now the pure-Python definitions remain and can be patched normally.

If you're using Pytest then include the snippet above in conftest.py and you can patch datetime objects normally.

GrantJ
  • 8,162
  • 3
  • 52
  • 46
4

Generally speaking, you would have datetime or perhaps datetime.date imported into a module somewhere. A more effective way of mocking the method would be to patch it on the module that is importing it. Example:

a.py

from datetime import date

def my_method():
    return date.today()

Then for your test, the mock object itself would be passed as an argument to the test method. You would set up the mock with the result value you want, and then call your method under test. Then you would assert that your method did what you want.

>>> import mock
>>> import a
>>> @mock.patch('a.date')
... def test_my_method(date_mock):
...     date_mock.today.return_value = mock.sentinel.today
...     result = a.my_method()
...     print result
...     date_mock.today.assert_called_once_with()
...     assert mock.sentinel.today == result
...
>>> test_my_method()
sentinel.today

A word of warning. It is most certainly possible to go overboard with mocking. When you do, it makes your tests longer, harder to understand, and impossible to maintain. Before you mock a method as simple as datetime.date.today, ask yourself if you really need to mock it. If your test is short and to the point and works fine without mocking the function, you may just be looking at an internal detail of the code you're testing rather than an object you need to mock.

jpmc26
  • 28,463
  • 14
  • 94
  • 146
  • I have build on your idea but I am patching my_method like that: date_mock.return_value = datetime.strptime('28 May 2130', '%d %b %Y'). It works like a charm. Thanks – Samir Sadek Jul 29 '20 at 16:31
1

It's possible to mock functions from datetime module without adding side_effects

import mock
from datetime import datetime
from where_datetime_used import do

initial_date = datetime.strptime('2018-09-27', "%Y-%m-%d")
with mock.patch('where_datetime_used.datetime') as mocked_dt:
    mocked_dt.now.return_value = initial_date
    do()
Danil
  • 4,781
  • 1
  • 35
  • 50
1

For those of you using pytest with pytest-mock (more info on pytest-mock at the end) here is how I mocked datetime.datetime.now() which is very similar to the original question.

test_get_now(mocker):
    datetime_mock = mocker.patch("blackline_accounts_import.datetime",)
    datetime_mock.datetime.now.return_value=datetime.datetime(2019,3,11,6,2,0,0)
    
    now == function_being_tested()  # run function

    assert now == datetime.datetime(2019,3,11,6,2,0,0)

Essentially the mock has to be set to return the specified date. You aren't able to patch over datetime's object directly.

Pytest-mock is a library that makes a mock object a fixture. More detail can be found here

Daniel Butler
  • 3,239
  • 2
  • 24
  • 37
  • I believe `mocker` is from pytest-mock. It might be nice to give a link/explain a bit. – mike rodent Oct 30 '21 at 12:06
  • Good point! I always install it with pytest, so I never think of them separately. – Daniel Butler Oct 31 '21 at 00:01
  • Thanks. I'm a novice with these Python mocks, patches, etc., so I think I have enough to try to understand already with modules unittest, pytest and pytest-qt before I try one day to understand the need for even more tools. Mind you, looking at that page I see it provides a "spy". In the Java/Groovy/Spock universe I tended to use Spy a lot. Although usually seasoned TDD hands seem to say you should probably never ever need a Spy, I always found them very useful. – mike rodent Oct 31 '21 at 08:27
  • @mikerodent totally get it. It took me a while to even want to try to use them. Pytest-mock is only a thin wrapper around unitest's mocks and the patch functionality. Once you get down the unittest mock pytest-mock removes a bit of boilerplate code - nothing special. I found this very helpful when starting to tackle mocks https://realpython.com/python-mock-library/ – Daniel Butler Nov 02 '21 at 20:50
  • I'm not sure why this is not working.. even if I add `my_module.callable = callable_mock`. What could be the problem ? – Ebram Shehata May 27 '22 at 10:45
  • The error you are trying to solve sounds like you are patching a function with the datetime object. Figuring out exactly what is wrong will go a long way in understanding mocks. Also, now a days I use this library to mock datetime https://pypi.org/project/pytest-freezegun/ – Daniel Butler May 28 '22 at 12:49
1

The best approach for me is a combination of @Daniel G and @frx08 solutions:

class Test_mock_date:
    class NewDate(datetime.datetime):
        @classmethod
        def now(cls, tz=None):
            return cls(2021, 5, 12)

    def test_mock_date(self):
        with patch('datetime.datetime', new = self.NewDate):
            assert datetime.datetime.now() == datetime.datetime(2021, 5, 12, 0, 0)

You can take a look at the following medium article I wrote with different examples about how to use MagicMock https://medium.com/@camposer/d2113513b365

camposer
  • 5,152
  • 2
  • 17
  • 15
0

Maybe you could use your own "today()" method that you will patch where needed. Example with mocking utcnow() can be found here: https://bitbucket.org/k_bx/blog/src/tip/source/en_posts/2012-07-13-double-call-hack.rst?at=default

Jens Timmerman
  • 9,316
  • 1
  • 42
  • 48
Konstantine Rybnikov
  • 2,457
  • 1
  • 22
  • 29
0

I implemented @user3016183 method using a custom decorator:

def changeNow(func, newNow = datetime(2015, 11, 23, 12, 00, 00)):
    """decorator used to change datetime.datetime.now() in the tested function."""
    def retfunc(self):                             
        with mock.patch('mymodule.datetime') as mock_date:                         
            mock_date.now.return_value = newNow
            mock_date.side_effect = lambda *args, **kw: datetime(*args, **kw)
            func(self)
    return retfunc

I thought that might help someone one day...

DainDwarf
  • 1,651
  • 10
  • 19
0

Several solutions are discussed in http://blog.xelnor.net/python-mocking-datetime/. In summary:

Mock object - Simple and efficient but breaks isinstance() checks:

target = datetime.datetime(2009, 1, 1)
with mock.patch.object(datetime, 'datetime', mock.Mock(wraps=datetime.datetime)) as patched:
    patched.now.return_value = target
    print(datetime.datetime.now())

Mock class

import datetime
import mock

real_datetime_class = datetime.datetime

def mock_datetime_now(target, dt):
    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

    # Python2 & Python3 compatible metaclass
    MockedDatetime = DatetimeSubclassMeta('datetime', (BaseMockedDatetime,), {})

    return mock.patch.object(dt, 'datetime', MockedDatetime)

Use as:

with mock_datetime_now(target, datetime):
   ....
eddygeek
  • 4,236
  • 3
  • 25
  • 32
0

I made this work by importing datetime as realdatetime and replacing the methods I needed in the mock with the real methods:

import datetime as realdatetime

@mock.patch('datetime')
def test_method(self, mock_datetime):
    mock_datetime.today = realdatetime.today
    mock_datetime.now.return_value = realdatetime.datetime(2019, 8, 23, 14, 34, 8, 0)
0

You can mock datetime using this:

In the module sources.py:

import datetime


class ShowTime:
    def current_date():
        return datetime.date.today().strftime('%Y-%m-%d')

In your tests.py:

from unittest import TestCase, mock
import datetime


class TestShowTime(TestCase):
    def setUp(self) -> None:
        self.st = sources.ShowTime()
        super().setUp()

    @mock.patch('sources.datetime.date')
    def test_current_date(self, date_mock):
        date_mock.today.return_value = datetime.datetime(year=2019, month=10, day=1)
        current_date = self.st.current_date()
        self.assertEqual(current_date, '2019-10-01')
MTMobile
  • 61
  • 1
  • 5
  • 1
    what is `sources` in your patch decorator? – elena Jun 30 '20 at 17:58
  • Dear @elena, it is rather hard to remember what I was thinking about almost one year ago)). I guess I meant just any module of our app sources - just the code of your application. – MTMobile Jul 04 '20 at 12:04
0

For those using patchers in a test class, here's how I'm successfully patching datetime functionality:

from datetime import datetime
import unittest
from unittest.mock import Mock, patch

# Replace with the proper path to the module you would
# like datetime to be mocked
from path.to.my_module

class MyTestCases(unittest.TestCase):

    def setUp(self):
        """execute on class instantiation"""
        # Record both times at the same moment
        self.dt_now, self.dt_utcnow = datetime.now(), datetime.utcnow()

        # After retrieving real (or hardcoded datetime values), 
        # proceed to mock them in desired module
        self.patch_datetime_functions()


    def patch_datetime_functions(self) -> None:
        """
        Patch datetime.now() and datetime.utcnow() to prevent issues when
        comparing expected dates
        """

        # Create a patcher
        self.patcher_dt = patch(
            'path.to.my_module'
        )

        # Start but make sure cleanup always occurs
        self.patcher_dt.start()
        self.addCleanup(self.patcher_dt.stop)

        # Perform the actual patch – use lambdas as mock functions
        datetime_mock = Mock(wraps=datetime)
        datetime_mock.now.return_value = self.dt_now
        datetime_mock.utcnow.return_value = self.dt_utcnow

        my_module.datetime = datetime_mock


    # Here's what it will look like when testing:
    def some_test(self):
        curr_dt = self.dt_now
        returned_dt = my_module.datetime.utcnow()
        
        # Compare the dates
        self.assertEqual(curr_dt, returned_dt,
            'Datetime values should be equal'
        )

Yaakov Bressler
  • 9,056
  • 2
  • 45
  • 69
0

Minimal working example with monkeypatch

This solution uses monkeypatch from the https://pypi.org/project/pytest-mock/ package.

Features:

  • mock only datetime.today(), but datetime.now() still works as expected
  • mock only in a specific scope (i.e. block)
import sys
from datetime import datetime

MOCKED_DATETIME_TODAY = datetime(1900, 1, 1, 0, 0, 0)

class MockedDatetime(datetime):
    @classmethod
    def today(cls):
        return MOCKED_DATETIME_TODAY

def test_mock_datetime_today(monkeypatch):
    """Only datetime.today() is mocked and returns some date in 1900. datetime.now() returns still the current date."""
    with monkeypatch.context() as mpc:
        mpc.setattr(sys.modules[__name__], 'datetime', MockedDatetime)
        assert datetime.today() == MOCKED_DATETIME_TODAY  # datetime.today() mocked
        assert datetime.now() > MOCKED_DATETIME_TODAY    # datetime.now() not mocked

    assert datetime.today() > MOCKED_DATETIME_TODAY  # not mocked anymore
Markus Dutschke
  • 9,341
  • 4
  • 63
  • 58
0

Refer this - https://guicommits.com/how-to-mock-datetime-today/

Worked for me, basically mocking the imported datetime from the module

  • Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Jul 02 '23 at 21:32
  • While this link may answer the question, it is better to include the essential parts of the answer here and provide the link for reference. Link-only answers can become invalid if the linked page changes. - [From Review](/review/late-answers/34624854) – geanakuch Jul 05 '23 at 11:58