谁能告诉我为什么这不起作用?
>>> 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)
也许有人可以提出更好的方法?
Answers:
有一些问题。
首先,您使用的方式mock.patch
不太正确。当用作装饰器时,它仅在装饰函数内datetime.date.today
用Mock
对象替换给定的函数/类(在这种情况下为)。因此,只有在您将是一个不同的功能,不会出现你想要的。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.date并创建合适的函数:
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)
datetime
实例恢复为其原始值?与deepcoppy
?
patch('mymodule.datetime', Mock(today=lambda: date(2017, 11, 29)))
@patch('module_you_want_to_test.date', Mock( today=Mock(return_value=datetime.date(2017, 11, 29))))
。
另一种选择是使用 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
它还会影响其他模块的方法调用中的其他日期时间调用:
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
对于它的价值,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)
...
from datetime import date
该模块,那么它就是模块的名称,from datetime import date
并在其中date.today()
显示
我想我来晚了一点,但是我认为这里的主要问题是您今天正在直接修补datetime.date.today,根据文档,这是错误的。
例如,您应该修补导入到已测试功能所在文件中的引用。
假设您有一个functions.py文件,其中包含以下内容:
import datetime
def get_today():
return datetime.date.today()
然后,在测试中,您应该有这样的内容
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...
希望这会有所帮助。
NameError: name 'datetime' is not defined
)。如果您不导入测试文件,则datetime.strptime
引用Mock(return_value=...)
来自何处datetime
?更新:没关系,我只是继续将datetime
模块导入测试文件中。我以为诀窍在于如何datetime
从测试文件中隐藏引用。
import datetime
还是from datetime import strptime
?如果您正在做第一个,则必须进行模拟,datetime
而do mocked_datetime.strptime.return_value = whatever
是后一个,则必须直接在测试方法所在的文件中模拟strptime引用。
Mock(return_value=datetime...)
工作的datetime引用。
要添加到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.date对象,但也可以对其进行更改。
@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)
date
/ datetime
本身,它使用全球可用的变量,所以应该没有问题:dpaste.com/790310
几天前我遇到了同样的情况,我的解决方案是在模块中定义一个函数进行测试并对其进行模拟:
def get_date_now():
return datetime.datetime.now()
今天我发现有关FreezeGun的信息,似乎可以很好地涵盖此案例
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)
对我来说,最简单的方法是:
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)
注意此解决方案:从所有的功能datetime module
从target_module
停止工作。
datetime_mock.now = Mock(return_value=datetime(1999, 1, 1)
甚至可以缩短为datetime_mock.now.return_value = datetime(1999, 1, 1)
。可以start()
考虑使用with patch(...):
上下文管理器确保datetime
测试结束后再次表现正常(而不模拟),而不是使用来启动补丁。
datetime.datetime.now()
不受嘲笑的^^?
datetime module
从target_module
停止工作。
您可以基于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)
基本上,我们用datetime.date
自己的python子类替换基于C的类,该子类产生原始datetime.date
实例并isinstance()
完全像native一样响应查询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()
功能。
__instancecheck__
方法获得最大递归深度RuntimeError 。
一般来说,您可能已经datetime
或可能将其datetime.date
导入到某个模块中。模拟该方法的一种更有效的方法是将其修补在要导入的模块上。例:
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
,请问问自己是否真的需要模拟它。如果您的测试很简短,并且在不模拟功能的情况下可以正常工作,则您可能只是查看要测试的代码的内部细节,而不是需要模拟的对象。
这是另一种模拟方法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
注意wraps=datetime
参数mock.patch()
–当foo_module
使用其他datetime
功能时,date.today()
它们将被转发到原始包装的datetime
模块。
在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):
....
也许您可以使用自己的“ today()”方法,将在需要的地方进行修补。可以在此处找到模拟utcnow()的示例:https ://bitbucket.org/k_bx/blog/src/tip/source/en_posts/2012-07-13-double-call-hack.rst?at=default
我使用自定义装饰器实现了@ 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
我以为有一天可以帮助某人...
可以从datetime
模块中模拟功能而无需添加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()
对于那些使用pytest与嘲笑者的人,这里是我如何嘲笑的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)
本质上,模拟必须设置为返回指定的日期。您无法直接修补datetime的对象。
您可以datetime
使用以下方法进行模拟:
在模块中sources.py
:
import datetime
class ShowTime:
def current_date():
return datetime.date.today().strftime('%Y-%m-%d')
在您的tests.py
:
from unittest import TestCase, mock
import datetime
class TestShowTime(TestCase):
def setUp(self) -> None:
self.st = sources.ShowTime()
super().setUp()
@mock.patch('sources.datetime.date')
def test_current_date(self, date_mock):
date_mock.today.return_value = datetime.datetime(year=2019, month=10, day=1)
current_date = self.st.current_date()
self.assertEqual(current_date, '2019-10-01')
sources
在你的补丁装饰?
CPython实际上使用纯Python Lib / datetime.py和C优化的模块/_datetimemodule.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
正常修补对象。
mock
库文件:voidspace.org.uk/python/mock/examples.html#partial-mocking