尝试使用返回self的__iter__方法模拟iterable时的RecursionError

问题描述 投票:0回答:1

出于单元测试的目的,我创建了一个类,其实例是一个可迭代的类,它将产生一个特定的序列然后引发一个异常:

class Iter:
    def __init__(self, seq):
        self.seq = seq
        self.pos = 0

    def __next__(self):
        if self.pos == len(self.seq):
            raise Exception
        value = self.seq[self.pos]
        self.pos += 1
        return value

    def __iter__(self):
        return self

以便:

for value in Iter((1, 2, 3)):
    print(value)

输出:

1
2
3
Traceback (most recent call last):
  File "test.py", line 25, in <module>
    for value in mocked_iterable:
  File "test.py", line 11, in __next__
    raise Exception
Exception

但是,当MagicMock已经具有应该做同样的side_effect属性时,为什么要重新发明轮子呢?根据documentationside_effect属性可以是一个可迭代的,它产生一个从模拟调用返回的值,或者一个异常来提升,所以它适合模仿上述类的完美目的。因此,我创建了一个MagicMock对象并使其__iter__方法返回对象本身,并使其__next__方法具有所需序列的副作用和异常:

from unittest.mock import MagicMock
mocked_iterable = MagicMock()
mocked_iterable.__iter__.return_value = mocked_iterable
mocked_iterable.__next__.side_effect = [1, 2, 3, Exception]
for value in mocked_iterable:
    print(value)

但是,这输出:

...
  File "C:\Program Files (x86)\Python36-32\lib\unittest\mock.py", line 1005, in _mock_call
    ret_val = effect(*args, **kwargs)
  File "C:\Program Files (x86)\Python36-32\lib\unittest\mock.py", line 1793, in __iter__
    return iter(ret_val)
  File "C:\Program Files (x86)\Python36-32\lib\unittest\mock.py", line 939, in __call__
    return _mock_self._mock_call(*args, **kwargs)
  File "C:\Program Files (x86)\Python36-32\lib\unittest\mock.py", line 944, in _mock_call
    self.called = True
RecursionError: maximum recursion depth exceeded

但问题是,为什么会有任何递归?

我发现我可以通过将自引用放在__iter__side_effect属性中来解决这个“bug”:

mocked_iterable = MagicMock()
mocked_iterable.__iter__.side_effect = [mocked_iterable]
mocked_iterable.__next__.side_effect = [1, 2, 3, Exception]
for value in mocked_iterable:
    print(value)

这正确输出:

1
2
3
Traceback (most recent call last):
  File "test.py", line 6, in <module>
    for value in mocked_iterable:
  File "C:\Program Files (x86)\Python36-32\lib\unittest\mock.py", line 939, in __call__
    return _mock_self._mock_call(*args, **kwargs)
  File "C:\Program Files (x86)\Python36-32\lib\unittest\mock.py", line 1000, in _mock_call
    raise result
Exception

但是递归错误确实是一个错误,或者mock的一个特征是否带有意想不到的后果?

python python-3.x python-mock
1个回答
0
投票

我同意这确实是一个错误。虽然这是一个边缘案例。

正如我们在源代码中看到的那样。 mock模块期望iter(ret_val)将返回未更改的迭代器,如果ret_val已经是迭代器。

嗯,实际上它确实需要调用ret_val__iter__方法。

© www.soinside.com 2019 - 2024. All rights reserved.