python随笔 Mock vs MagicMock

2019-01-20  本文已影响0人  辰辰沉沉沉

首先,从现象上看,Mock有的MagicMock都有,而MagicMock多了一些magic方法。

_mock = mock.Mock()
_magicmock = mock.MagicMock()
mock.__iter__ # error
_magicmock.__iter__ #<MagicMock name='mock.__iter__' id='4458398224'>

而且MagicMock是懒加载的,在没调用magic方法的时候,是不存在这个方法的。

>>> _magicmock = mock.MagicMock()
>>> dir(_magicmock)
['assert_any_call', 'assert_called', 'assert_called_once'...
>>> iter(_magicmock)
<listiterator object at 0x10e32e290>
>>> dir(_magicmock)
['__iter__', 'assert_any_call', 'assert_called'...

很神奇。

所以在使用上,除了那种需要调用魔术方法报错的(如不允许迭代)都可以使用MagicMock。如果保守点,可以都使用Mock,其实也都够用来着。


下边进入正题,在看MagicMock中看到的有意思的东西。下边会回答MagicMock对魔术方法的懒加载是如何实现的。

首先,magicmock是mock的一个子类。

class MagicMock(MagicMixin, Mock)

很明显,实现设置magic方法的在MagicMixin中。

class MagicMixin(object):
    def __init__(self, *args, **kw):
        self._mock_set_magics()  # make magic work for kwargs in init
        _safe_super(MagicMixin, self).__init__(*args, **kw)
        self._mock_set_magics()  # fix magic broken by upper level init


    def _mock_set_magics(self):
        these_magics = _magics

        if getattr(self, "_mock_methods", None) is not None:
            these_magics = _magics.intersection(self._mock_methods)

            remove_magics = set()
            remove_magics = _magics - these_magics

            for entry in remove_magics:
                if entry in type(self).__dict__:
                    # remove unneeded magic methods
                    delattr(self, entry)

        # don't overwrite existing attributes if called a second time
        these_magics = these_magics - set(type(self).__dict__)

        _type = type(self)
        for entry in these_magics:
            setattr(_type, entry, MagicProxy(entry, self))

这段代码也不长,直接贴上来了。

可以看到,通过一串东西,找到了所有需要添加的魔术方法,然后给这个类赋予了一个MagicProxy。这个proxy会在第一次调用的时候创建魔术方法。

这边有个问题,直接获取type赋值,为啥不会影响其他的mock嘞?很简单,每个magicmock的type都不一样。

>>> type(mock.MagicMock()) == type(mock.MagicMock())
False

直觉告诉我,是什么地方有修改__new__的地方。果然,在很上层的父类,NonCallableMock中找到了这么个东西。

    def __new__(cls, *args, **kw):
        # every instance has its own class
        # so we can create magic methods on the
        # class without stomping on other mocks
        new = type(cls.__name__, (cls,), {'__doc__': cls.__doc__})
        instance = object.__new__(new)
        return instance

看注释也很清楚了,就是为了magic方法才做的这个事,在创建的时候做了些手脚。

结论

把上面的过程顺起来说一遍。

上一篇下一篇

猜你喜欢

热点阅读