谁能告诉我为什么这行不通?
>>> 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)
也许有人能提出一个更好的办法?
当前回答
有一些问题。
首先,你使用模拟的方式。帕奇说得不太对。当用作装饰器时,它只在被装饰的函数内用Mock对象替换给定的函数/类(在本例中为datetime.date.today)。因此,只有在today()中,datetime.date.today才会是一个不同的函数,这似乎不是你想要的。
你真正想要的似乎是这样的:
@mock.patch('datetime.date.today')
def test():
datetime.date.today.return_value = date(2010, 1, 1)
print datetime.date.today()
不幸的是,这行不通:
>>> 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'
这是失败的,因为Python内置类型是不可变的-更多细节请参阅这个答案。
在本例中,我将继承datetime的子类。日期自己,并创建正确的函数:
import datetime
class NewDate(datetime.date):
@classmethod
def today(cls):
return cls(2010, 1, 1)
datetime.date = NewDate
现在你可以这样做:
>>> datetime.date.today()
NewDate(2010, 1, 1)
其他回答
对我来说最简单的方法是:
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)
注意:target_module中的datetime模块中的所有功能都将停止工作。
也许您可以使用自己的“today()”方法,在需要的地方打补丁。使用utcnow()的示例可以在这里找到:https://bitbucket.org/k_bx/blog/src/tip/source/en_posts/2012-07-13-double-call-hack.rst?at=default
值得注意的是,Mock文档专门讨论了datetime.date.today,并且可以在不创建虚拟类的情况下做到这一点:
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)
...
一般来说,你会有datetime或datetime。将日期导入某个模块。模拟该方法的一种更有效的方法是在导入它的模块上修补它。例子:
a.py
from datetime import date
def my_method():
return date.today()
然后,对于您的测试,模拟对象本身将作为参数传递给测试方法。您将使用所需的结果值设置模拟,然后调用测试中的方法。然后您可以断言您的方法完成了您想要的。
>>> 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
警告一句。很有可能嘲讽得过火了。当您这样做时,它会使您的测试更长、更难理解,并且不可能维护。在模拟像datetime.date这样简单的方法之前。今天,问问自己是否真的需要嘲笑它。如果您的测试短小精干,并且在不模拟函数的情况下运行良好,那么您可能只看到了正在测试的代码的内部细节,而不是需要模拟的对象。
我们可以使用pytest-mock (https://pypi.org/project/pytest-mock/)模拟器对象来模拟特定模块中的日期时间行为
假设您想在以下文件中模拟日期时间
# File path - source_dir/x/a.py
import datetime
def name_function():
name = datetime.now()
return f"name_{name}"
在测试函数中,mock将在测试运行时添加到函数中
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"