谁能告诉我为什么这行不通?
>>> 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)
也许有人能提出一个更好的办法?
谁能告诉我为什么这行不通?
>>> 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)
也许有人能提出一个更好的办法?
当前回答
可以在不添加side_effects的情况下模拟datetime模块中的函数
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()
其他回答
在Daniel G的解决方案中补充一点:
from datetime import date
class FakeDate(date):
"A manipulable date replacement"
def __new__(cls, *args, **kwargs):
return date.__new__(date, *args, **kwargs)
这将创建一个类,当实例化时,该类将返回一个正常的datetime。对象,但也可以更改。
@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)
对于那些使用pytest和pytest-mock的人(关于pytest-mock的更多信息在最后),这里是我如何模拟datetime.datetime.now(),这与最初的问题非常相似。
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)
本质上,mock必须设置为返回指定的日期。您不能直接修补datetime的对象。
Pytest-mock是一个使模拟对象成为fixture的库。更多细节可以在这里找到
可以在不添加side_effects的情况下模拟datetime模块中的函数
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()
CPython实际上使用纯python Lib/datetime.py和c优化的Modules/ _datetimmodule .c实现了datetime模块。c优化版本不能打补丁,但纯python版本可以。
在Lib/datetime.py中纯python实现的底部是这样的代码:
try:
from _datetime import * # <-- Import from C-optimized module.
except ImportError:
pass
这段代码导入了所有c优化的定义,并有效地替换了所有纯python定义。我们可以通过以下方式强制CPython使用datetime模块的纯python实现:
import datetime
import importlib
import sys
sys.modules["_datetime"] = None
importlib.reload(datetime)
通过设置sys。modules["_datetime"] = None,我们告诉Python忽略c优化的模块。然后重新加载导致从_datetime导入失败的模块。现在纯python定义仍然存在,可以正常打补丁。
如果你正在使用Pytest,那么在conftest.py中包含上面的代码片段,你就可以正常地修补datetime对象了。
下面是另一种模拟datetime.date.today()的方法,其额外的好处是其余的datetime函数继续工作,因为模拟对象被配置为包装原始的datetime模块:
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
注意mock.patch()的wraps=datetime参数——当foo_module使用date.today()之外的其他datetime函数时,它们将被转发到原始包装的datetime模块。