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

>>> 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和@frx08的解决方案:

class Test_mock_date:
    class NewDate(datetime.datetime):
        @classmethod
        def now(cls, tz=None):
            return cls(2021, 5, 12)

    def test_mock_date(self):
        with patch('datetime.datetime', new = self.NewDate):
            assert datetime.datetime.now() == datetime.datetime(2021, 5, 12, 0, 0)

你可以看看下面我写的关于如何使用MagicMock的不同示例的中型文章https://medium.com/@camposer/d2113513b365

其他回答

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

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

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模块中的所有功能都将停止工作。

我使用自定义装饰器实现了@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

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

下面是另一种模拟datetime.date.today()的方法,其额外的好处是其余的datetime函数继续工作,因为模拟对象被配置为包装原始的datetime模块:

from unittest import mock, TestCase

import foo_module

class FooTest(TestCase):

    @mock.patch(f'{foo_module.__name__}.datetime', wraps=datetime)
    def test_something(self, mock_datetime):
        # mock only datetime.date.today()
        mock_datetime.date.today.return_value = datetime.date(2019, 3, 15)
        # other calls to datetime functions will be forwarded to original datetime

注意mock.patch()的wraps=datetime参数——当foo_module使用date.today()之外的其他datetime函数时,它们将被转发到原始包装的datetime模块。

CPython实际上使用纯python Lib/datetime.py和c优化的Modules/ _datetimmodule .c实现了datetime模块。c优化版本不能打补丁,但纯python版本可以。

在Lib/datetime.py中纯python实现的底部是这样的代码:

try:
    from _datetime import *  # <-- Import from C-optimized module.
except ImportError:
    pass

这段代码导入了所有c优化的定义,并有效地替换了所有纯python定义。我们可以通过以下方式强制CPython使用datetime模块的纯python实现:

import datetime
import importlib
import sys

sys.modules["_datetime"] = None
importlib.reload(datetime)

通过设置sys。modules["_datetime"] = None,我们告诉Python忽略c优化的模块。然后重新加载导致从_datetime导入失败的模块。现在纯python定义仍然存在,可以正常打补丁。

如果你正在使用Pytest,那么在conftest.py中包含上面的代码片段,你就可以正常地修补datetime对象了。