出于单元测试的目的,我制作了一个其实例是可迭代的类,该类将产生一定的序列,然后引发异常:
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
属性时,为什么要重新发明轮子呢?对于documentation,side_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
的一项功能,具有意想不到的后果?
答案 0 :(得分:0)
我同意这确实是一个错误。尽管这是一个极端的情况。
我们在源代码中可以看到。 mock
模块期望iter(ret_val)
已经是迭代器的情况下,ret_val
将返回不变的迭代器。
嗯,它确实可以,但是仍然需要调用ret_val
的{{1}}方法。