Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python PropertyMock side effect with AttributeError and ValueError

Tags:

I am trying to mock a property of a class (@property decorator) and have bumped into this incorrect behaviour:

 >>> from mock import MagicMock, PropertyMock
 >>> m = MagicMock()
 >>> type(m).p = PropertyMock(side_effect=AttributeError)
 >>> m.p
 <MagicMock name='mock.p' id='63150736'>

The correct behaviour is this:

 >>> from mock import MagicMock, PropertyMock
 >>> m = MagicMock()
 >>> type(m).p = PropertyMock(side_effect=ValueError)
 >>> m.p
Traceback (most recent call last)
[...]
ValueError

I cannot fathom why setting a different exception is giving me different results. The expected result in both cases is that the exception should be raised! So, the In[4] line should raise an AttributeError. It does not.

Anyone care to enlighten me?

Addendum: The property I am trying to check does some clever checking to see if the value passed is sane. If said value is not sane, it returns AttributeError as I understand that this is the correct exception in Python. So, I need to check the code that uses the property for failure as well as success. Thus, using a MagicMock to mock the property and raise said exception. A trivial example would be:

@x.setter
def x(self, value):
    if value < 0:
         raise AttributeError("Value cannot be negative!")
    self._x = value
like image 501
Sardathrion - against SE abuse Avatar asked Aug 14 '13 15:08

Sardathrion - against SE abuse


2 Answers

I know this question is old, but I've just had the same problem and found this question. Also the bug report submitted almost two years ago didn't seem to get any attention, so I thought I'll share the solution I found just in case anybody else will have this problem.

So, as stated PropertyMock doesn't work with AttributeError set as a side_effect. The workaround is to create a simple Mock with a spec attribute set to an empty list like this:

>>> from mock import Mock
>>> m = Mock(spec=[])
>>> m.p
Traceback (most recent call last)
[...]
AttributeError

As stated in the docs:

spec: This can be either a list of strings or an existing object (a class or instance) that acts as the specification for the mock object. If you pass in an object then a list of strings is formed by calling dir on the object (excluding unsupported magic attributes and methods). Accessing any attribute not in this list will raise an AttributeError.

like image 71
Max Tepkeev Avatar answered Sep 22 '22 15:09

Max Tepkeev


Er, hold the phone. Does this cover your use case?

>>> import mock
>>> m = mock.MagicMock()
>>> m.p
<MagicMock name='mock.p' id='139756843423248'>
>>> del m.p #!
>>> m.p
Traceback (most recent call last):
     File "<stdin>", line 1, in <module>
     File "/home/ahammel/bin/python/mock-1.0.1-py2.6.egg/mock.py", line 664, in __getattr__
         raise AttributeError(name)
     AttributeError: p

I stumbled across that in the docs while looking for something entirely different.

like image 43
Alex Hammel Avatar answered Sep 25 '22 15:09

Alex Hammel