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

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

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


当前回答

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

其他回答

值得注意的是,Mock文档专门讨论了datetime.date.today,并且可以在不创建虚拟类的情况下做到这一点:

https://docs.python.org/3/library/unittest.mock-examples.html#partial-mocking

>>> from datetime import date
>>> with patch('mymodule.date') as mock_date:
...     mock_date.today.return_value = date(2010, 10, 8)
...     mock_date.side_effect = lambda *args, **kw: date(*args, **kw)
...
...     assert mymodule.date.today() == date(2010, 10, 8)
...     assert mymodule.date(2009, 6, 8) == date(2009, 6, 8)
...

你可以用这个来模拟datetime:

在sources.py模块中:

import datetime


class ShowTime:
    def current_date():
        return datetime.date.today().strftime('%Y-%m-%d')

在您的tests.py中:

from unittest import TestCase, mock
import datetime


class TestShowTime(TestCase):
    def setUp(self) -> None:
        self.st = sources.ShowTime()
        super().setUp()

    @mock.patch('sources.datetime.date')
    def test_current_date(self, date_mock):
        date_mock.today.return_value = datetime.datetime(year=2019, month=10, day=1)
        current_date = self.st.current_date()
        self.assertEqual(current_date, '2019-10-01')

也许您可以使用自己的“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"

另一种选择是使用 https://github.com/spulec/freezegun/

安装:

pip install freezegun

并使用它:

from freezegun import freeze_time

@freeze_time("2012-01-01")
def test_something():

    from datetime import datetime
    print(datetime.now()) #  2012-01-01 00:00:00

    from datetime import date
    print(date.today()) #  2012-01-01

它还会影响其他模块的方法调用中的其他datetime调用:

other_module.py:

from datetime import datetime

def other_method():
    print(datetime.now())    

main.py:

from freezegun import freeze_time

@freeze_time("2012-01-01")
def test_something():

    import other_module
    other_module.other_method()

最后:

$ python main.py
# 2012-01-01