尝试使用返回自身的__iter__方法模拟可迭代对象时发生RecursionError

时间:2019-04-19 23:27:05

标签: python python-3.x python-mock

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

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__的{​​{1}}属性中来解决此“错误”:

side_effect

这正确输出:

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 的一项功能,具有意想不到的后果?

1 个答案:

答案 0 :(得分:0)

我同意这确实是一个错误。尽管这是一个极端的情况。

我们在源代码中可以看到。 mock模块期望iter(ret_val)已经是迭代器的情况下,ret_val将返回不变的迭代器。

嗯,它确实可以,但是仍然需要调用ret_val的{​​{1}}方法。