Python mock setter property when wrapping it
How do you mock a python property setter when porting it (i.e. calling the original setter)? The most direct way is to access __set__
, but it is read-only for properties, so it doesn't work.
from unittest import TestCase
from unittest.mock import patch, PropertyMock
class SomeClass:
def __init__(self, value):
self._value = value
@property
def value(self):
return self._value + 1
@value.setter
def value(self, value):
self._value = value + 1
class TestSomeClass(TestCase):
def test_value_setter(self):
instance = SomeClass(0)
with patch.object(SomeClass.value, '__set__', wraps=SomeClass.value.__set__) as value_setter:
instance.value = 1
value_setter.assert_called_with(instance, 1)
self.assertEquals(instance.value, 3)
There is also in the docs new_callable=PropertyMock
, I tried to combine it with wrap
but didn't get it yet.
source to share
You need to replace the whole object property
; you can't just replace the installer. this means you will need to provide a new object property
where the setter is your layout wrapper for the original setter (attribute access property.fset
):
setter_mock = Mock(wraps=SomeClass.value.fset)
mock_property = SomeClass.value.setter(setter_mock)
with patch.object(SomeClass, 'value', mock_property):
instance.value = 1
setter_mock.assert_called_with(instance, 1)
I've used the decorator @property.setter
here again ; it returns a new object property
with all the attributes of the original property
, except that the setter is replaced with what was passed; see How does the @property decorator work?
source to share
Here's another solution that reuses PropertyMock
but only has the drawback of allowing setters to mock in context.
with patch.object(SomeClass, 'value', new_callable=PropertyMock, wraps=partial(
SomeClass.value.__set__, instance)
) as value:
instance.value = 1
value.assert_called_with(1)
self.assertEquals(instance.value, 3)
or
instance = SomeClass(0)
with patch.object(SomeClass, 'value', PropertyMock(
side_effect=partial(SomeClass.value.__set__, instance)
)) as value:
instance.value = 1
value.assert_called_with(1)
self.assertEquals(instance.value, 3)
source to share