MagicMock 如何避免在调用随机方法时抛出 AttributeError?



在Python中,如果你调用一个不存在的方法,它会抛出一个AttributeError。前任

>>> class A:
...     def yo(self):
...             print(1)
... 
>>> a = A()
>>> a.yo()
1
>>> a.hello()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'A' object has no attribute 'hello'

在下面的代码中,MagicMock 类没有名为 hello 的函数,或者没有为方法 hello 创建补丁。仍然在代码下面不会抛出属性错误

>>> from unittest.mock import MagicMock 
>>> obj = MagicMock()
>>> obj.hello()
<MagicMock name='mock.hello()' id='4408758568'>

MagicMock是如何做到这一点的?如何创建一个类,该类可以在调用任何方法(可能未定义(时执行操作?

Python 数据模型记录了一个钩子,__getattr__,当属性访问无法以通常的方式解析时调用。模拟使用它来返回一个新的模拟实例 - 即模拟将未知属性定义为工厂

以更简单的方式重现 mock 的实现,您只需将__getattr____call__转换为工厂函数:

class M:
def __call__(self):
return M()
def __getattr__(self, name):
return M()

用法示例:

>>> mock = M()
>>> mock.potato
<__main__.M at 0xdeadbeef>
>>> mock.potato()
<__main__.M at 0xcafef00d>

MagicMock是如何做到这一点的?

这部分不是特定于MagicMock的,普通Mock也会这样做(名称中的"魔法"只是指允许更好地模拟魔法方法的附加功能(。MagicMock从其中一个基类继承此类行为:

>>> MagicMock.mro()
[unittest.mock.MagicMock,
unittest.mock.MagicMixin,
unittest.mock.Mock,
unittest.mock.CallableMixin,
unittest.mock.NonCallableMock,  # <--- this one overrides __getattr__!
unittest.mock.Base,
object]

如何创建一个类,该类可以在调用任何方法(可能未定义(时执行操作?

这取决于您是要位于正常属性访问的前面还是后面。 如果你想走在前面,你应该定义__getattribute__,在搜索类/实例命名空间之前,无条件地调用它来实现属性访问。但是,如果要对普通属性(即对象__dict__中的属性(和描述符采用较低的优先级,则应定义__getattr__如前所述。

我实际上不知道MagicMock具体是如何工作的(我从未使用过它,但我听说过好事(,但是这部分行为可以通过劫持__getattr__来复制(以及可能还有其他多种可能的解决方案(,它返回一个可调用对象,该可调用对象在调用时创建一个新的模拟实例:

class MM:
def __init__(self, name=None):
# store a name, TODO: random id, etc.
self.name = name
def __repr__(self):
# make it pretty
if self.name:
r = f'<MM name={self.name}>'
else:
r = f'<MM>'
return r
def __getattr__(self, attrname):
# we want a factory for a mock instance with a name corresponding to attrname
def magicattr():
return MM(name=f"'mock.{attrname}()'")
return magicattr

执行时,我们看到以下内容:

>>> MM()
<MM>
>>> MM().hello()
<MM name='mock.hello()'>

我并没有过分定义id之类的,但基本的技巧可以在上面的精简示例中看到。

上述工作方式是访问.hello或任何其他属性通过我们的自定义__getattr__这使我们有机会动态生成具有我们想要的任何属性的虚假(模拟(方法。据我了解,MagicMock的众多好处之一是我们不必担心默认情况下抛出AttributeError,它只是有效

相关内容

  • 没有找到相关文章

最新更新