为什么可以在unittest.mock.mock.configure_mock中设置dunder/魔术方法?

发布于 2025-01-28 14:06:28 字数 3520 浏览 3 评论 0原文

我在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})

读取回溯我们看到的是:

  1. callablemixin的__ init __ INT __ INT __被调用。
  2. Kwargs传递给了Mock的__ INT __方法将传递给 mock.configure_mock()
  3. 名称是getaTtr()
  4. 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:

  1. CallableMixin's __init__ is called.
  2. kwargs passed to Mock's __init__ method get passed to Mock.configure_mock()
  3. the kwargs name is getattr()ed on the Mock itself
  4. 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文