How do you mock a python property setter while wrapping it (i.e. calling the original setter)? The most straightward way is to access __set__
, but it's read-only for properties so 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's also the new_callable=PropertyMock
in the docs, I've tried to combine it with wrap
but haven't gotten it to work yet.
Getters and Setters in python are often used when: We use getters & setters to add validation logic around getting and setting a value. To avoid direct access of a class field i.e. private variables cannot be accessed directly or modified by external user.
side_effect: A function to be called whenever the Mock is called. See the side_effect attribute. Useful for raising exceptions or dynamically changing return values. The function is called with the same arguments as the mock, and unless it returns DEFAULT , the return value of this function is used as the return value.
MagicMock. MagicMock objects provide a simple mocking interface that allows you to set the return value or other behavior of the function or object creation call that you patched. This allows you to fully define the behavior of the call and avoid creating real objects, which can be onerous.
You need to replace the whole property
object; you cannot simply replace the setter. this means you'll need to provide a new property
object, where the setter is your mock wrapper for the original setter (accessed by the property.fset
attribute):
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 reused the @property.setter
decorator here; it returns a new property
object with all attributes of the original property
except with the setter replaced by whatever was passed in; see How does the @property decorator work?
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With