Python:我如何模拟datetime.utcnow()?

时间:2017-05-05 07:41:01

标签: python unit-testing mocking

我有以下内容:

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()

上为我工作

4 个答案:

答案 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-timehttps://nedbatchelder.com/blog/201209/mocking_datetimetoday.htmlhttps://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')