How to make fun of readonly property with layout?

How do you mock readonly with mock ?

I tried:

setattr(obj.__class__, 'property_to_be_mocked', mock.Mock()) 

but the problem is that it applies to all instances of the class ... which breaks my tests.

Do you have another idea? I do not want to scoff at a complete object, only this specific property.

+64
python unit-testing mocking
Aug 06 '12 at 21:45
source share
5 answers

I think the best way is to make fun of the property as a PropertyMock rather than mock the __get__ method.

The documentation states unittest.mock.PropertyMock : A layout designed to be used as a property or other descriptor for a class. PropertyMock provides the __get__ and __set__ , so you can specify the return value when retrieving it.

Here's how:

 class MyClass: @property def last_transaction(self): # an expensive and complicated DB query here pass def 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() 
+115
Aug 21 '14 at 10:28
source share

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

Here's how to do it:

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

In the test case:

 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 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 
+39
Aug 07 '12 at 10:17
source share

Probably a matter of style, but if 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 pass class Test(unittest.TestCase): @mock.patch('MyClass.last_transaction', new_callable=PropertyMock) def test(mock_last_transaction): mock_last_transaction.return_value = Transaction() myclass = MyClass() print myclass.last_transaction mock_last_transaction.assert_called_once_with() 
+3
Jan 10 '17 at 14:28
source share

If you do not want to check if the property being mocked was accessible, you can simply fix it with the expected return_value .

 with mock.patch(MyClass, 'last_transaction', Transaction()): ... 
0
Mar 07 '17 at 0:46
source share

If the object whose property you want to override is a dummy object, you do not need to use patch .

Instead, you can create a PropertyMock and then override the layout type property. For example, to override the mock_rows.pages property to return (mock_page, mock_page,) :

 mock_page = mock.create_autospec(reader.ReadRowsPage) # TODO: set up mock_page. mock_pages = mock.PropertyMock(return_value=(mock_page, mock_page,)) type(mock_rows).pages = mock_pages 
0
Apr 11 '19 at 23:18
source share



All Articles