谁能告诉我为什么这行不通?

>>> import mock
>>> @mock.patch('datetime.date.today')
... def today(cls):
...  return date(2010, 1, 1)
...
>>> from datetime import date
>>> date.today()
datetime.date(2010, 12, 19)

也许有人能提出一个更好的办法?


当前回答

http://blog.xelnor.net/python-mocking-datetime/中讨论了几种解决方案。总而言之:

模拟对象-简单有效,但打破isinstance()检查:

target = datetime.datetime(2009, 1, 1)
with mock.patch.object(datetime, 'datetime', mock.Mock(wraps=datetime.datetime)) as patched:
    patched.now.return_value = target
    print(datetime.datetime.now())

模拟类

import datetime
import mock

real_datetime_class = datetime.datetime

def mock_datetime_now(target, dt):
    class DatetimeSubclassMeta(type):
        @classmethod
        def __instancecheck__(mcs, obj):
            return isinstance(obj, real_datetime_class)

    class BaseMockedDatetime(real_datetime_class):
        @classmethod
        def now(cls, tz=None):
            return target.replace(tzinfo=tz)

        @classmethod
        def utcnow(cls):
            return target

    # Python2 & Python3 compatible metaclass
    MockedDatetime = DatetimeSubclassMeta('datetime', (BaseMockedDatetime,), {})

    return mock.patch.object(dt, 'datetime', MockedDatetime)

使用:

with mock_datetime_now(target, datetime):
   ....

其他回答

我通过将datetime导入为realdatetime并将我在模拟中需要的方法替换为实际方法来完成这项工作:

import datetime as realdatetime

@mock.patch('datetime')
def test_method(self, mock_datetime):
    mock_datetime.today = realdatetime.today
    mock_datetime.now.return_value = realdatetime.datetime(2019, 8, 23, 14, 34, 8, 0)

http://blog.xelnor.net/python-mocking-datetime/中讨论了几种解决方案。总而言之:

模拟对象-简单有效,但打破isinstance()检查:

target = datetime.datetime(2009, 1, 1)
with mock.patch.object(datetime, 'datetime', mock.Mock(wraps=datetime.datetime)) as patched:
    patched.now.return_value = target
    print(datetime.datetime.now())

模拟类

import datetime
import mock

real_datetime_class = datetime.datetime

def mock_datetime_now(target, dt):
    class DatetimeSubclassMeta(type):
        @classmethod
        def __instancecheck__(mcs, obj):
            return isinstance(obj, real_datetime_class)

    class BaseMockedDatetime(real_datetime_class):
        @classmethod
        def now(cls, tz=None):
            return target.replace(tzinfo=tz)

        @classmethod
        def utcnow(cls):
            return target

    # Python2 & Python3 compatible metaclass
    MockedDatetime = DatetimeSubclassMeta('datetime', (BaseMockedDatetime,), {})

    return mock.patch.object(dt, 'datetime', MockedDatetime)

使用:

with mock_datetime_now(target, datetime):
   ....

也许您可以使用自己的“today()”方法,在需要的地方打补丁。使用utcnow()的示例可以在这里找到:https://bitbucket.org/k_bx/blog/src/tip/source/en_posts/2012-07-13-double-call-hack.rst?at=default

我们可以使用pytest-mock (https://pypi.org/project/pytest-mock/)模拟器对象来模拟特定模块中的日期时间行为

假设您想在以下文件中模拟日期时间

# File path - source_dir/x/a.py
import datetime

def name_function():
     name = datetime.now()
     return f"name_{name}"

在测试函数中,mock将在测试运行时添加到函数中

def test_name_function(mocker):
     mocker.patch('x.a.datetime')
     x.a.datetime.now.return_value = datetime(2019, 1, 1)

     actual = name_function()

     assert actual == "name_2019-01-01"

几天前我遇到了同样的情况,我的解决方案是在模块中定义一个函数来测试和模拟:

def get_date_now():
    return datetime.datetime.now()

今天我发现了《FreezeGun》,它似乎很好地解决了这个问题

from freezegun import freeze_time
import datetime
import unittest


@freeze_time("2012-01-14")
def test():
    assert datetime.datetime.now() == datetime.datetime(2012, 1, 14)