[英]How to mock python's datetime.now() in a class method for unit testing?
我正在尝试为具有以下方法的类编写测试:
import datetime
import pytz
class MyClass:
def get_now(self, timezone):
return datetime.datetime.now(timezone)
def do_many_things(self, tz_string='Europe/London'):
tz = pytz.timezone(tz_string)
localtime_now = self.get_now(tz)
...
return things
我想测试它,为此我需要确保datetime.datetime.now()
调用返回可预测的内容。
我一直在阅读许多在测试中使用Mock的示例,但没有找到与我需要的完全相同的东西,而且我无法弄清楚如何在我的测试中使用它。
我将get_now()
方法分开,以防它更容易模拟它,而不是datetime.datetime.now()
,但我仍然很难过。 关于如何使用 Mock 为此编写单元测试的任何想法? (这一切都在 Django 中,fwiw;我不确定在这种情况下这是否会有所不同。)
你可以使用freezegun :
from freezegun import freeze_time
def test():
assert datetime.datetime.now() != datetime.datetime(2012, 1, 14)
with freeze_time("2012-01-14"):
assert datetime.datetime.now() == datetime.datetime(2012, 1, 14)
assert datetime.datetime.now() != datetime.datetime(2012, 1, 14)
它基本上模拟datetime
模块调用。
您将创建一个返回特定日期时间的函数,本地化为传入的时区:
import mock
def mocked_get_now(timezone):
dt = datetime.datetime(2012, 1, 1, 10, 10, 10)
return timezone.localize(dt)
@mock.patch('path.to.your.models.MyClass.get_now', side_effect=mocked_get_now)
def your_test(self, mock_obj):
# Within this test, `MyClass.get_now()` is a mock that'll return a predictable
# timezone-aware datetime object, set to 2012-01-01 10:10:10.
这样您就可以测试是否正确处理了生成的时区感知日期时间; 其他地方的结果应该显示正确的时区,但会有一个可预测的日期和时间。
在mocked_get_now
您使用mocked_get_now
函数作为get_now
; 每当代码调用get_now
,调用都会被mock
记录,并且mocked_get_now
被调用,它的返回值用作返回给get_now
调用者的get_now
。
我正在使用date
,但同样的想法应该适用于datetime
:
class SpoofDate(date):
def __new__(cls, *args, **kwargs):
return date.__new__(date, *args, **kwargs)
...
from mock import patch
@patch('some.module.date', SpoofDate)
def testSomething(self):
SpoofDate.today = classmethod(lambda cls : date(2012, 9, 24))
some.module
导入date
。 Patch 正在用SpoofDate
替换导入的date
,然后您可以重新定义它以执行您想要的任何操作。
我将使用“testfixtures”包中的帮助程序来模拟您正在调用 now() 的日期时间类:
http://packages.python.org/testfixtures/datetime.html#datetimes
这样,您就可以一直测试您拥有的所有案例。
本来问这个问题...
正如@Jocelyn delalande 所建议的那样,多年来我一直很高兴地使用freezegun 。
另一种选择是python-libfaketime ,它可以比 freezegun 快得多,但在 Windows 上不起作用,听起来有点繁琐。
较新的选项是time-machine ,在这篇博客文章中介绍了它比较了三个选项。
使用 unittest.mock 的补丁
from unittest.mock import patch
@patch('MyClass.datetime')
def test_foo(self, mock_datetime):
mock_datetime.datetime.now.return_value = datetime.datetime(2019, 5, 7) #SOME_MOCKED_DATE
请注意,我们正在覆盖仅在我们的类中导入的 datetime 模块
我们正在为其编写测试的类:
import datetime
class MyClass:
def foo():
localtime_now = datetime.datetime.now(timezone)
我们不必将它作为 get_now() 方法分开,只是为了更容易模拟。
如果您不想安装任何东西,这是最简单的方法。 简单使用,Mock 类——
class NewDt(datetime.date):
@classmethod
def now(cls):
return datetime.datetime.strptime('2020-07-10 05:20:20', '%Y-%m-%d %H:%M:%S')
并在模拟功能之前使用此补丁
@mock.patch('module path', NewDt)
这是执行此 IMO 的最优雅的方法:
import datetime
from unittest import mock
test_now = datetime.datetime(1856, 7, 10)
with mock.patch('datetime.datetime', wraps=datetime.datetime) as dt:
print(dt.now()) # calls the real thing
dt.now.return_value = test_now
print(dt.now()) # calls the mocked value
这里的优点是您不需要通过测试模块的本地属性修补 datetime 模块,它支持调用 unmocked 方法,并且不需要任何外部导入。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.