如何在类方法中模拟python的datetime.now()进行单元测试?

时间:2012-10-25 16:41:24

标签: python django unit-testing testing mocking

我正在尝试为具有以下方法的类编写测试:

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为此编写UnitTests的任何想法? (这一切都在Django,fwiw;我不确定这是否会对这种情况产生影响。)

7 个答案:

答案 0 :(得分:33)

您可以使用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模块调用。

答案 1 :(得分:22)

您将创建一个返回特定日期时间的函数,该日期时间本地化为传入的时区:

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时,您使用get_now函数作为副作用;每当代码调用get_now时,mock都会记录调用,调用 mocked_get_now,并将其返回值用作返回给调用者{{}的值1}}。

答案 2 :(得分:4)

我正在使用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的位置。修补程序正在使用date替换导入的SpoofDate,然后您可以重新定义以执行任何操作。

答案 3 :(得分:2)

我会使用'testfixtures'包中的帮助器来模拟你现在调用的日期时间类():

http://packages.python.org/testfixtures/datetime.html#datetimes

这样,您可以随时测试所有案例。

答案 4 :(得分:1)

如果您不想安装任何东西,这是最简单的方法。只需使用, 模拟课-

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)

答案 5 :(得分:0)

  

使用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()方法分开。

答案 6 :(得分:-1)

最初问过这个问题...

就像@Jocelyn delalande所建议的那样,多年来我一直很开心地使用freezegun

另一个选择是python-libfaketime,它可以比freezegun快得多,但是在Windows上不起作用,听起来有些古怪。

time-machine中引入了一个较新的选项this blog post,用于比较这三个选项。