谁能告诉我为什么这行不通?
>>> 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)
也许有人能提出一个更好的办法?
当前回答
另一种选择是使用 https://github.com/spulec/freezegun/
安装:
pip install freezegun
并使用它:
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
它还会影响其他模块的方法调用中的其他datetime调用:
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()
最后:
$ python main.py
# 2012-01-01
其他回答
您可以使用以下基于Daniel G解的方法。这个方法的优点是不会破坏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)
基本上,我们取代了基于c语言的datetime。Date类,使用我们自己的python子类,生成原始的datetime。日期实例,并响应isinstance()查询完全与本机datetime.date相同。
在测试中使用它作为上下文管理器:
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)
类似的方法可用于模拟datetime.datetime.now()函数。
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):
....
我使用自定义装饰器实现了@user3016183方法:
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
我想这也许有一天能帮到别人…
对我来说,最好的方法是结合@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
我们可以使用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"