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

Phi*_*ord 38 python testing django unit-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
Run Code Online (Sandbox Code Playgroud)

我想测试它,为此我需要确保datetime.datetime.now()调用返回可预测的内容.

我一直在阅读很多在测试中使用Mock的例子,但是没有发现任何我需要的东西,我无法弄清楚如何在我的测试中使用它.

我把get_now()方法分开了,以防它更容易嘲笑,而不是datetime.datetime.now(),但我仍然难倒.有关如何使用Mock为此编写UnitTests的任何想法?(这一切都在Django,fwiw;我不确定这是否会对这种情况产生影响.)

Joc*_*nde 35

你可以使用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)
Run Code Online (Sandbox Code Playgroud)

它基本上datetime模拟了模块调用.


Mar*_*ers 24

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

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.
Run Code Online (Sandbox Code Playgroud)

这样,您可以测试是否正确处理了生成时区的日期时间; 其他地方的结果应显示正确的时区,但具有可预测的日期和时间.

mocked_get_now模拟时使用该函数作为副作用get_now; 每当代码调用时get_now,调用都被记录mock,并被 mocked_get_now调用,并且它的返回值被用作返回给调用者的值get_now.


bla*_*ais 8

在我看来,这是最优雅的方法:

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
Run Code Online (Sandbox Code Playgroud)

这里的优点是你不需要通过被测试模块的本地属性来修补 datetime 模块,它支持调用未模拟的方法,并且不需要任何外部导入。


Izk*_*ata 5

我正在使用date,但同样的想法应该适用于datetime

class SpoofDate(date):
    def __new__(cls, *args, **kwargs):
        return date.__new__(date, *args, **kwargs)
Run Code Online (Sandbox Code Playgroud)

...

from mock import patch

@patch('some.module.date', SpoofDate)
def testSomething(self):
    SpoofDate.today = classmethod(lambda cls : date(2012, 9, 24))
Run Code Online (Sandbox Code Playgroud)

哪里some.module进口date。Patch 正在用 替换导入dateSpoofDate,然后您可以重新定义它以执行您想要的任何操作。