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

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

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


当前回答

对于那些在测试类中使用补丁程序的人,下面是我如何成功地修补datetime功能:

from datetime import datetime
import unittest
from unittest.mock import Mock, patch

# Replace with the proper path to the module you would
# like datetime to be mocked
from path.to.my_module

class MyTestCases(unittest.TestCase):

    def setUp(self):
        """execute on class instantiation"""
        # Record both times at the same moment
        self.dt_now, self.dt_utcnow = datetime.now(), datetime.utcnow()

        # After retrieving real (or hardcoded datetime values), 
        # proceed to mock them in desired module
        self.patch_datetime_functions()


    def patch_datetime_functions(self) -> None:
        """
        Patch datetime.now() and datetime.utcnow() to prevent issues when
        comparing expected dates
        """

        # Create a patcher
        self.patcher_dt = patch(
            'path.to.my_module'
        )

        # Start but make sure cleanup always occurs
        self.patcher_dt.start()
        self.addCleanup(self.patcher_dt.stop)

        # Perform the actual patch – use lambdas as mock functions
        datetime_mock = Mock(wraps=datetime)
        datetime_mock.now.return_value = self.dt_now
        datetime_mock.utcnow.return_value = self.dt_utcnow

        my_module.datetime = datetime_mock


    # Here's what it will look like when testing:
    def some_test(self):
        curr_dt = self.dt_now
        returned_dt = my_module.datetime.utcnow()
        
        # Compare the dates
        self.assertEqual(curr_dt, returned_dt,
            'Datetime values should be equal'
        )

其他回答

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

另一种选择是使用 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

对于那些使用pytest和pytest-mock的人(关于pytest-mock的更多信息在最后),这里是我如何模拟datetime.datetime.now(),这与最初的问题非常相似。

test_get_now(mocker):
    datetime_mock = mocker.patch("blackline_accounts_import.datetime",)
    datetime_mock.datetime.now.return_value=datetime.datetime(2019,3,11,6,2,0,0)
    
    now == function_being_tested()  # run function

    assert now == datetime.datetime(2019,3,11,6,2,0,0)

本质上,mock必须设置为返回指定的日期。您不能直接修补datetime的对象。

Pytest-mock是一个使模拟对象成为fixture的库。更多细节可以在这里找到

对我来说,最好的方法是结合@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

可以在不添加side_effects的情况下模拟datetime模块中的函数

import mock
from datetime import datetime
from where_datetime_used import do

initial_date = datetime.strptime('2018-09-27', "%Y-%m-%d")
with mock.patch('where_datetime_used.datetime') as mocked_dt:
    mocked_dt.now.return_value = initial_date
    do()