Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

python mock property setter while wrapping it

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.

like image 847
simonzack Avatar asked Jan 09 '15 15:01

simonzack


People also ask

What is the most pythonic way to use getter and setter?

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.

What is Side_effect in mock Python?

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.

What is Python MagicMock?

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.


1 Answers

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?

like image 161
Martijn Pieters Avatar answered Oct 23 '22 11:10

Martijn Pieters