【问题标题】:Why am I getting RecursionError: maximum recursion depth exceeded when mocking datetime.date behaviour?为什么在模拟 datetime.date 行为时出现 RecursionError: maximum recursion depth exceeded?
【发布时间】:2021-07-02 10:04:38
【问题描述】:

我在unittest docs 的以下示例中模拟模块sample.py 中的以下函数:

import datetime
from random import randint

def useless_date():
    today_date = datetime.date.today()
    x = randint(0,1000)
    dt = datetime.timedelta(days = x)

    return today_date + dt

我已经设置了一个 test_file,我注意到 python 中有一些我不理解的奇怪行为。如果使用显式 datetimedelta 导入,测试运行得非常好:

from unittest import mock
from sample import useless_date
from datetime import date, timedelta

@mock.patch("sample.datetime.date")
@mock.patch("sample.datetime.timedelta")
def test_useless_date(mock_timedelta, mock_date):
    mock_date.today.return_value = date(1999, 1, 1)
    mock_timedelta.return_value = timedelta(days=1)
    mock_date.side_effect = lambda *args, **kwargs: date(*args, **kwargs)

    assert useless_date() == date(1999, 1, 2)

但是,如果我只导入 datetime 模块而不显式导入 datetimedelta,则会出现递归错误。

代码:

from unittest import mock
from sample import useless_date
import datetime

@mock.patch("sample.datetime.date")
@mock.patch("sample.datetime.timedelta")
def test_useless_date(mock_timedelta, mock_date):
    date = datetime.date
    timedelta = datetime.timedelta
    mock_date.today.return_value = date(1999, 1, 1)
    mock_timedelta.return_value = timedelta(days=1)
    mock_date.side_effect = lambda *args, **kwargs: date(*args, **kwargs)

    assert useless_date() == date(1999, 1, 2)

错误:

__________________________________________________________________________________________________________ test_useless_date ___________________________________________________________________________________________________________

mock_timedelta = <MagicMock name='timedelta' id='4466974048'>, mock_date = <MagicMock name='date' id='4467058096'>

    @mock.patch("sample.datetime.date")
    @mock.patch("sample.datetime.timedelta")
    def test_useless_date(mock_timedelta, mock_date):
        date = datetime.date
        timedelta = datetime.timedelta
        mock_date.today.return_value = date(1999, 1, 1)
        mock_timedelta.return_value = timedelta(days=1)
        mock_date.side_effect = lambda *args, **kwargs: date(*args, **kwargs)
    
>       assert useless_date() == date(1999, 1, 2)

test_sample.py:60: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.8/lib/python3.8/unittest/mock.py:1081: in __call__
    return self._mock_call(*args, **kwargs)
/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.8/lib/python3.8/unittest/mock.py:1085: in _mock_call
    return self._execute_mock_call(*args, **kwargs)
/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.8/lib/python3.8/unittest/mock.py:1146: in _execute_mock_call
    result = effect(*args, **kwargs)
test_sample.py:58: in <lambda>
    mock_date.side_effect = lambda *args, **kwargs: date(*args, **kwargs)
/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.8/lib/python3.8/unittest/mock.py:1081: in __call__
    return self._mock_call(*args, **kwargs)
E   RecursionError: maximum recursion depth exceeded
!!! Recursion detected (same locals & position)
======================================================================================================= short test summary info ========================================================================================================
FAILED test_sample.py::test_useless_date - RecursionError: maximum recursion depth exceeded


Python 版本:Python 3.8.2

【问题讨论】:

    标签: python-3.x unit-testing python-import python-unittest


    【解决方案1】:

    在您的测试中发生递归是因为当您编写 date = datetime.date 时,您并没有得到您认为得到的结果(原始的 datetime.date 函数),而是模拟的函数。

    就像你在测试函数中做import datetime一样,使用的模块是全局模块,所以写@mock.patch("sample.datetime.date")和写@mock.patch("datetime.date")的效果是一样的——你正在修补全局datetime.date函数。在你的代码中这意味着datemock_date一样,你的副作用和写一样

    mock_date.side_effect = lambda *args, **kwargs: mock_date(*args, **kwargs)
    

    这显然会导致递归。

    如果您使用 from datetime import date,则不会发生这种情况,因为在这种情况下,date 是对 datetime.date 的新引用,位于导入它的模块中。因此,在您的第一个版本中,date 变量指的是test_sample.date(假设test_sample 是测试模块名称)而不是datetime.date。如果您在sample.py 中使用from datetime import date,也会发生同样的情况。在这种情况下,您必须修补 sample.date,这与 datetime.date 不同,并且您的第二个测试也可以工作。

    【讨论】:

    • 感谢您的解释!
    猜你喜欢
    • 1970-01-01
    • 2013-03-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-04-19
    • 1970-01-01
    • 2022-08-20
    • 2018-10-23
    相关资源
    最近更新 更多