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

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

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


当前回答

在Daniel G的解决方案中补充一点:

from datetime import date

class FakeDate(date):
    "A manipulable date replacement"
    def __new__(cls, *args, **kwargs):
        return date.__new__(date, *args, **kwargs)

这将创建一个类,当实例化时,该类将返回一个正常的datetime。对象,但也可以更改。

@mock.patch('datetime.date', FakeDate)
def test():
    from datetime import date
    FakeDate.today = classmethod(lambda cls: date(2010, 1, 1))
    return date.today()

test() # datetime.date(2010, 1, 1)

其他回答

monkeypatch的最小工作示例

这个解决方案使用https://pypi.org/project/pytest-mock/包中的monkeypatch。

特点:

仅模拟datetime.today(),但datetime.now()仍然可以正常工作 仅在特定范围内模拟(即块)

import sys
from datetime import datetime

MOCKED_DATETIME_TODAY = datetime(1900, 1, 1, 0, 0, 0)

class MockedDatetime(datetime):
    @classmethod
    def today(cls):
        return MOCKED_DATETIME_TODAY

def test_mock_datetime_today(monkeypatch):
    """Only datetime.today() is mocked and returns some date in 1900. datetime.now() returns still the current date."""
    with monkeypatch.context() as mpc:
        mpc.setattr(sys.modules[__name__], 'datetime', MockedDatetime)
        assert datetime.today() == MOCKED_DATETIME_TODAY  # datetime.today() mocked
        assert datetime.now() > MOCKED_DATETIME_TODAY    # datetime.now() not mocked

    assert datetime.today() > MOCKED_DATETIME_TODAY  # not mocked anymore

我通过将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)

我使用自定义装饰器实现了@user3016183方法:

def changeNow(func, newNow = datetime(2015, 11, 23, 12, 00, 00)):
    """decorator used to change datetime.datetime.now() in the tested function."""
    def retfunc(self):                             
        with mock.patch('mymodule.datetime') as mock_date:                         
            mock_date.now.return_value = newNow
            mock_date.side_effect = lambda *args, **kw: datetime(*args, **kw)
            func(self)
    return retfunc

我想这也许有一天能帮到别人…

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

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)

对我来说最简单的方法是:

import datetime
from unittest.mock import Mock, patch

def test():
    datetime_mock = Mock(wraps=datetime.datetime)
    datetime_mock.now.return_value = datetime.datetime(1999, 1, 1)
    with patch('datetime.datetime', new=datetime_mock):
        assert datetime.datetime.now() == datetime.datetime(1999, 1, 1)

注意:target_module中的datetime模块中的所有功能都将停止工作。