How to mock a readonly property with mock? How to mock a readonly property with mock? python python

How to mock a readonly property with mock?


I think the better way is to mock the property as PropertyMock, rather than to mock the __get__ method directly.

It is stated in the documentation, search for unittest.mock.PropertyMock:A mock intended to be used as a property, or other descriptor, on a class. PropertyMock provides __get__ and __set__ methods so you can specify a return value when it is fetched.

Here is how:

class MyClass:    @property    def last_transaction(self):        # an expensive and complicated DB query here        passdef test(unittest.TestCase):    with mock.patch('MyClass.last_transaction', new_callable=PropertyMock) as mock_last_transaction:        mock_last_transaction.return_value = Transaction()        myclass = MyClass()        print myclass.last_transaction        mock_last_transaction.assert_called_once_with()


Actually, the answer was (as usual) in the documentation, it's just that I was applying the patch to the instance instead of the class when I followed their example.

Here is how to do it:

class MyClass:    @property    def last_transaction(self):        # an expensive and complicated DB query here        pass

In the test suite:

def test():    # Make sure you patch on MyClass, not on a MyClass instance, otherwise    # you'll get an AttributeError, because mock is using settattr and    # last_transaction is a readonly property so there's no setter.    with mock.patch(MyClass, 'last_transaction') as mock_last_transaction:        mock_last_transaction.__get__ = mock.Mock(return_value=Transaction())        myclass = MyClass()        print myclass.last_transaction


Probably a matter of style but in case you prefer decorators in tests, @jamescastlefield's answer could be changed to something like this:

class MyClass:    @property    def last_transaction(self):        # an expensive and complicated DB query here        passclass Test(unittest.TestCase):    @mock.patch('MyClass.last_transaction', new_callable=PropertyMock)    def test(self, mock_last_transaction):        mock_last_transaction.return_value = Transaction()        myclass = MyClass()        print myclass.last_transaction        mock_last_transaction.assert_called_once_with()