为什么可以在unittest.mock.mock.configure_mock中设置dunder/魔术方法?
我在python写了一些单位测试,需要嘲笑一个非常通用的 collections.abc.sized ,所以我开始使用__ len __
方法创建一个模拟,其返回值是我想要的任何int。
from unittest.mock import Mock
sized = Mock(**{"__len__.return_value": 8})
attributeErrorgave失败:__len __
在帖子结束时产生了追溯,而以下工作:
from unittest.mock import Mock
sized = Mock()
sized.__len__.return_value = 8
# or
from unittest.mock import MagicMock
sized = MagicMock(**{"__len__.return_value": 8})
读取回溯我们看到的是:
- callablemixin的
__ init __ INT __ INT __
被调用。 - Kwargs传递给了Mock的
__ INT __
方法将传递给mock.configure_mock()
- 名称是
getaTtr()
- kwargs 方法如果属性名称是魔术,则可以提出属性,也就是说,如果
'__%s__'%name [2:-2] ==名称
。
这似乎是一个非常任意的限制,我想知道模拟是否有理由不接受 mock.configure_mock()
。
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
Input In [2], in <cell line: 1>()
----> 1 Mock(**{"__len__.return_value": 0})
File ~/.pyenv/versions/3.10.3/lib/python3.10/unittest/mock.py:1086, in CallableMixin.__init__(self, spec, side_effect, return_value, wraps, name, spec_set, parent, _spec_state, _new_name, _new_parent, **kwargs)
1082 def __init__(self, spec=None, side_effect=None, return_value=DEFAULT,
1083 wraps=None, name=None, spec_set=None, parent=None,
1084 _spec_state=None, _new_name='', _new_parent=None, **kwargs):
1085 self.__dict__['_mock_return_value'] = return_value
-> 1086 _safe_super(CallableMixin, self).__init__(
1087 spec, wraps, name, spec_set, parent,
1088 _spec_state, _new_name, _new_parent, **kwargs
1089 )
1091 self.side_effect = side_effect
File ~/.pyenv/versions/3.10.3/lib/python3.10/unittest/mock.py:457, in NonCallableMock.__init__(self, spec, wraps, name, spec_set, parent, _spec_state, _new_name, _new_parent, _spec_as_instance, _eat_self, unsafe, **kwargs)
454 __dict__['_mock_unsafe'] = unsafe
456 if kwargs:
--> 457 self.configure_mock(**kwargs)
459 _safe_super(NonCallableMock, self).__init__(
460 spec, wraps, name, spec_set, parent,
461 _spec_state
462 )
File ~/.pyenv/versions/3.10.3/lib/python3.10/unittest/mock.py:625, in NonCallableMock.configure_mock(self, **kwargs)
623 obj = self
624 for entry in args:
--> 625 obj = getattr(obj, entry)
626 setattr(obj, final, val)
File ~/.pyenv/versions/3.10.3/lib/python3.10/unittest/mock.py:636, in NonCallableMock.__getattr__(self, name)
634 raise AttributeError("Mock object has no attribute %r" % name)
635 elif _is_magic(name):
--> 636 raise AttributeError(name)
637 if not self._mock_unsafe:
638 if name.startswith(('assert', 'assret', 'asert', 'aseert', 'assrt')):
AttributeError: __len__
I was writing some unit test in python and needed to mock a very general collections.abc.Sized, so I set off creating a Mock with a __len__
method whose return value is any int I want.
from unittest.mock import Mock
sized = Mock(**{"__len__.return_value": 8})
Which failed with AttributeErrorgave: __len__
and produced the traceback at the end of the post, while the following works:
from unittest.mock import Mock
sized = Mock()
sized.__len__.return_value = 8
# or
from unittest.mock import MagicMock
sized = MagicMock(**{"__len__.return_value": 8})
Reading down the traceback we see that:
- CallableMixin's
__init__
is called. - kwargs passed to Mock's
__init__
method get passed toMock.configure_mock()
- the kwargs name is
getattr()
ed on the Mock itself - Mock
__getattr__
method raises an AttributeError if attribute name is magic, that is if'__%s__' % name[2:-2] == name
.
This seems like a very arbitrary limitation and I wonder if there is a reason for Mock not to accept magic methods in Mock.configure_mock()
.
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
Input In [2], in <cell line: 1>()
----> 1 Mock(**{"__len__.return_value": 0})
File ~/.pyenv/versions/3.10.3/lib/python3.10/unittest/mock.py:1086, in CallableMixin.__init__(self, spec, side_effect, return_value, wraps, name, spec_set, parent, _spec_state, _new_name, _new_parent, **kwargs)
1082 def __init__(self, spec=None, side_effect=None, return_value=DEFAULT,
1083 wraps=None, name=None, spec_set=None, parent=None,
1084 _spec_state=None, _new_name='', _new_parent=None, **kwargs):
1085 self.__dict__['_mock_return_value'] = return_value
-> 1086 _safe_super(CallableMixin, self).__init__(
1087 spec, wraps, name, spec_set, parent,
1088 _spec_state, _new_name, _new_parent, **kwargs
1089 )
1091 self.side_effect = side_effect
File ~/.pyenv/versions/3.10.3/lib/python3.10/unittest/mock.py:457, in NonCallableMock.__init__(self, spec, wraps, name, spec_set, parent, _spec_state, _new_name, _new_parent, _spec_as_instance, _eat_self, unsafe, **kwargs)
454 __dict__['_mock_unsafe'] = unsafe
456 if kwargs:
--> 457 self.configure_mock(**kwargs)
459 _safe_super(NonCallableMock, self).__init__(
460 spec, wraps, name, spec_set, parent,
461 _spec_state
462 )
File ~/.pyenv/versions/3.10.3/lib/python3.10/unittest/mock.py:625, in NonCallableMock.configure_mock(self, **kwargs)
623 obj = self
624 for entry in args:
--> 625 obj = getattr(obj, entry)
626 setattr(obj, final, val)
File ~/.pyenv/versions/3.10.3/lib/python3.10/unittest/mock.py:636, in NonCallableMock.__getattr__(self, name)
634 raise AttributeError("Mock object has no attribute %r" % name)
635 elif _is_magic(name):
--> 636 raise AttributeError(name)
637 if not self._mock_unsafe:
638 if name.startswith(('assert', 'assret', 'asert', 'aseert', 'assrt')):
AttributeError: __len__
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论