谁能告诉我为什么这行不通?
>>> 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)
也许有人能提出一个更好的办法?
当前回答
对我来说,最好的方法是结合@Daniel G和@frx08的解决方案:
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)
你可以看看下面我写的关于如何使用MagicMock的不同示例的中型文章https://medium.com/@camposer/d2113513b365
其他回答
我通过将datetime导入为realdatetime并将我在模拟中需要的方法替换为实际方法来完成这项工作:
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)
monkeypatch的最小工作示例
这个解决方案使用https://pypi.org/project/pytest-mock/包中的monkeypatch。
特点:
仅模拟datetime.today(),但datetime.now()仍然可以正常工作 仅在特定范围内模拟(即块)
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
一般来说,你会有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这样简单的方法之前。今天,问问自己是否真的需要嘲笑它。如果您的测试短小精干,并且在不模拟函数的情况下运行良好,那么您可能只看到了正在测试的代码的内部细节,而不是需要模拟的对象。
也许您可以使用自己的“today()”方法,在需要的地方打补丁。使用utcnow()的示例可以在这里找到:https://bitbucket.org/k_bx/blog/src/tip/source/en_posts/2012-07-13-double-call-hack.rst?at=default
http://blog.xelnor.net/python-mocking-datetime/中讨论了几种解决方案。总而言之:
模拟对象-简单有效,但打破isinstance()检查:
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())
模拟类
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)
使用:
with mock_datetime_now(target, datetime):
....