我有以下内容:
from datetime import datetime
def get_report_month_key():
month_for_report = datetime.utcnow()
return month_for_report.strftime("%Y%m")
如何模拟datetime.utcnow()以便我可以在此函数上编写单元测试?
尝试阅读此one,但我无法让它在utcnow()
上为我工作答案 0 :(得分:21)
在您的测试文件中:
from yourfile import get_report_month_key
import mock
import unittest
from datetime import datetime
class TestCase(unittest.TestCase):
@mock.patch('yourfile.datetime')
def test_dt(self, mock_dt):
mock_dt.utcnow = mock.Mock(return_value=datetime(1901, 12, 21))
r = get_report_month_key()
self.assertEqual('190112', r)
答案 1 :(得分:6)
如果您在要测试的模块中未创建任何datetime
实例,则dasjotre接受的答案有效。如果尝试创建一个datetime
,它将创建一个Mock对象,而不是在标准datetime
对象上使用预期方法的对象。这是因为它用模拟代替了整个类的定义。代替执行此操作,您可以使用类似的方法,以datetime
为基础来创建模拟定义。
mymodule.py
from datetime import datetime
def after_y2k():
y2k = datetime(2000, 1, 1)
return y2k < datetime.utcnow()
test_mymodule.py
import unittest
import datetime
from mock import patch, Mock
import mymodule
from mymodule import after_y2k
class ModuleTests(unittest.TestCase):
@patch.object(mymodule, 'datetime', Mock(wraps=datetime.datetime))
def test_after_y2k_passes(self):
# Mock the return and run your test (Note you are doing it on your module)
mymodule.datetime.utcnow.return_value = datetime.datetime(2002, 01, 01)
self.assertEqual(True, after_y2k())
mymodule.datetime.utcnow.return_value = datetime.datetime(1999, 01, 01)
self.assertEqual(False, after_y2k())
@patch('mymodule.datetime')
def test_after_y2k_fails(self, mock_dt):
# Run your tests
mock_dt.utcnow = Mock(return_value=datetime.datetime(2002, 01, 01))
self.assertEqual(True, after_y2k())
# FAILS!!! because the object returned by utcnow is a MagicMock w/o
# datetime methods like "__lt__"
mock_dt.utcnow = Mock(return_value=datetime.datetime(1999, 01, 01))
self.assertEqual(False, after_y2k())
答案 2 :(得分:3)
修补内置Python模块时的效果也很复杂(与datetime
一样,请参阅例如https://solidgeargroup.com/mocking-the-time或https://nedbatchelder.com/blog/201209/mocking_datetimetoday.html或https://gist.github.com/rbarrois/5430921)将函数包装在一个自定义函数中,然后可以轻松修补。
因此,您不必调用datetime.datetime.utcnow()
,而是使用类似
import datetime
def get_utc_now():
return datetime.datetime.utcnow()
然后,修补这个就像
一样简单import datetime
# use whatever datetime you need here
fixed_now = datetime.datetime(2017, 8, 21, 13, 42, 20)
with patch('your_module_name.get_utc_now', return_value=fixed_now):
# call the code using get_utc_now() here
pass
使用patch
装饰器代替上下文管理器也可以起到相同的作用。
答案 3 :(得分:0)
您可以尝试使用冻结时间模块。
from yourfile import get_report_month_key
from freezegun import freeze_time
import unittest
class TestCase(unittest.TestCase):
@freeze_time('2017-05-01')
def get_report_month_key_test():
get_report_month_key().should.equal('201705')