我使用unittest.mock
来测试我的代码。如果一个对象不存在,我用hasattr
为它添加一个属性。
根据Python 3.6和更新版本的文档,我可以使用del
删除mock的属性,但它在Python 3.6上失败。在Python 3.7及以上版本中,它可以工作。
下面是一个简短的测试,显示了我要测试的内容:
from unittest.mock import Mock
def add_attribute(info):
if not hasattr(info, 'attribute'):
info.attribute = 'Attribute Added'
def test_mock():
info = Mock()
assert hasattr(info, 'attribute')
del info.attribute
assert not hasattr(info, 'attribute')
add_attribute(info)
assert info.attribute == 'Attribute Added'
del info.attribute
assert not hasattr(info, 'attribute')
在python 3.8中,它工作:
======= test session starts ========
platform linux -- Python 3.8.5, pytest-6.2.4, py-1.10.0, pluggy-0.13.1
rootdir: /code
plugins: cov-2.12.1
collected 1 item
test.py .
在python 3.6中,它失败:
============================= test session starts ==============================
platform linux -- Python 3.6.14, pytest-6.2.4, py-1.10.0, pluggy-0.13.1
rootdir: /code
plugins: cov-2.12.1
collected 1 item
test.py F [100%]
=================================== FAILURES ===================================
__________________________________ test_mock ___________________________________
def test_mock():
info = Mock()
assert hasattr(info, 'attribute')
del info.attribute
assert not hasattr(info, 'attribute')
add_attribute(info)
assert info.attribute == 'Attribute Added'
> del info.attribute
test.py:14:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
self = <Mock id='140171347130128'>, name = 'attribute'
def __delattr__(self, name):
if name in _all_magics and name in type(self).__dict__:
delattr(type(self), name)
if name not in self.__dict__:
# for magic methods that are still MagicProxy objects and
# not set on the instance itself
return
if name in self.__dict__:
object.__delattr__(self, name)
obj = self._mock_children.get(name, _missing)
if obj is _deleted:
> raise AttributeError(name)
E AttributeError: attribute
/usr/local/lib/python3.6/unittest/mock.py:728: AttributeError
=========================== short test summary info ============================
FAILED test.py::test_mock - AttributeError: attribute
============================== 1 failed in 0.11s ===============================
test_python exited with code 1
我不确定unittest库是否更新了,或者可能是(un)预期的对象在Python版本之间传递函数的行为。
请赐教。
在222d303中修复了bpo-20239的问题
这在d358a8c中被反向移植到3.7,d358a8c首次出现在python 3.7.3rc1
这个补丁对mock上的__delattr__
方法进行了重大更改
~ <3.7.3代码节选:
def __delattr__(self, name):
# ...
if name in self.__dict__:
object.__delattr__(self, name)
obj = self._mock_children.get(name, _missing)
if obj is _deleted:
raise AttributeError(name)
并与3.7.3+代码进行比较:
def __delattr__(self, name):
# ...
obj = self._mock_children.get(name, _missing)
if name in self.__dict__:
_safe_super(NonCallableMock, self).__delattr__(name)
elif obj is _deleted:
raise AttributeError(name)
在重复删除的情况下,3.6代码将触发AttributeError
(由于第一个删除设置_deleted
)—但是修复使得它可以避免(通过elif
)