Trying to simulate datetime.date.today () but not working

Can someone tell me why this is not 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 can suggest a better way?

+133
python datetime testing mocking
Dec 19 '10 at 6:54
source share
15 answers

There are several issues.

First of all, the way to use mock.patch not quite right. When used as a decorator, it replaces the given function / class (in this case, datetime.date.today ) with a Mock object only inside the decorated function. Thus, only in your today() will datetime.date.today be another function that does not seem necessary to you.

What you really want seems 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 will not 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 the Python built-in types are immutable - see this answer for more details.

In this case, I will subclass datetime.date myself and create the correct function:

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

And now you can do:

 >>> datetime.date.today() NewDate(2010, 1, 1) 
+107
Dec 19 '10 at 7:49
source share

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 
+138
Jul 14 '13 at 22:22
source share

Whatever the cost, the Mock documentation specifically talks about datetime.date.today, and this can be done 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) ... 
+91
03 Sep '14 at 20:01
source share

I think I was a little late for this, but I think the main problem is that you are fixing datetime.date.today directly, and according to the documentation, this is wrong.

You must fix the link imported into the file where, for example, the function is tested.

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 bit.

+33
Nov 17 '13 at 18:29
source share

To add Daniel G to the 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 that, when instantiated, will return a regular datetime.date object, but which can also be modified.

 @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) 
+30
Mar 25 '11 at 19:28
source share

You can use the following approach based on Daniel G.'s solution. This has the advantage of not violating 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 are replacing the C-based datetime.date class with our own python subclass, which creates the original datetime.date instances and responds to isinstance() requests just like native datetime.date .

Use it as a 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) 

A similar approach can be used to retrieve the datetime.datetime.now() function.

+6
Nov 22 '13 at 21:55
source share

I came across the same situation a couple of days ago, and my solution was to define a function in the module for testing and just laugh at this:

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

Today I learned about FreezeGun , and it seems that it perfectly covers this case.

 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) 
+6
Mar 14 '18 at 16:54
source share

The easiest way for me to do this:

 from unittest import patch, Mock def test(): datetime_mock = Mock(wraps=datetime) datetime_mock.now = Mock(return_value=datetime(1999, 1, 1) patch('target_module.datetime', new=datetime_mock).start() 

ATTENTION for this solution: all functions from the datetime module from target_module will stop working.

+4
Apr 17 '18 at 14:41
source share

Generally speaking, you have a datetime or possibly datetime.date , imported into a module somewhere. A more efficient way to mock this method would be to fix it on the module that imports it. Example:

a.py

 from datetime import date def my_method(): return date.today() 

Then, for your test, the layout object itself will be passed as an argument to the test method. You must configure the layout with the desired result value, and then call your method under testing. Then you claim 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. Of course, you can laugh at ridicule. When you do this, it makes your tests longer, harder to understand, and impossible to maintain. Before you scoff at a simple method like datetime.date.today , ask yourself if you really need to scoff at it. If your test is short and accurate and works fine without mocking the function, you can just look at the inside of the code under test, and not the object you need to make fun of.

+3
May 20 '13 at 19:52
source share

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

Mock object - simple and efficient, but breaks isinstance () check:

 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): .... 
+1
Apr 09 '17 at 20:46 on
source share

Perhaps you can use your own "today ()" method, which you will correct where necessary. An 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

0
Jul 14 '12 at 12:50
source share

I applied the @ 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 one day this might help someone ...

0
Nov 24 '15 at 12:55
source share

You can mock functions from the 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() 
0
Jan 24 '19 at 15:26
source share

Here is another way to simulate datetime.date.today() with the added bonus that the rest of the datetime functions continue to work, since the dummy 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 for mock.patch() - when foo_module uses other datetime functions other than date.today() they will be redirected to the original packed datetime module.

0
Mar 15 '19 at 17:28
source share

For those of you using pytest with mocker, here's 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 layout must be set to return the specified date. You cannot directly bind a datetime object.

0
Apr 11 '19 at 22:40
source share



All Articles