Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do you mock a RelatedManager method in Django?

I have a customer manager for a Django model which overrides the create method to also save some related objects:

class CustomManager(models.Manager):
    def create(self, amount, user, description):
        txn = self.get_query_set().create(user, description)
        txn.budget_transactions.create(amount)
        return txn

My question is: how do I mock the call to txn.budget_transactions.create to raise an exception?

The budget_transactions attribute of the txn object is an instance of django.db.models.fields.related.RelatedManager. Using mock.patch to mock this class doesn't work as it is declared dynamically - it can't be imported directly.

Does anyone know how to do this?

like image 943
DavidWinterbottom Avatar asked Aug 31 '12 09:08

DavidWinterbottom


2 Answers

The reason that you can't just set the RelatedManager to a mock object is because django has overridden the set method on the object. So while it appears that the mock is getting set correctly because there are no complaints, it's actually silently setting budget_transactions back to the RelatedManager. So if you really need to return a mock then you'll need to override the get method that returns the RelatedManager and return a mocked object instead.

Should end up lookig somthing like:

@mock.patch('django.db.models.fields.related.ForeignRelatedObjectsDescriptor.__get__')
def test_campaign_cancel(self, mock_manager):
    mock_manager.return_value = mock.MagicMock()
    mock_manager.return_value.create = Exception('Boom!')

That being said there are many pitfalls to this approach since it will be overriding a core django method and now ALL RelatedManagers will return a mocked object. From what I have experienced so far it's probably easier to explore other options.

like image 92
chrisst Avatar answered Nov 15 '22 23:11

chrisst


RelatedManager is a descriptor and has to be mocked using unittest.PropertyMock

from unittest import patch, MagicMock, PropertyMock, TestCase


class CustomManagerTest(TestCase):
    @patch('app.managers.Transaction.budget_transactions', new_callable=PropertyMock)
    def test_custom_manager_create(self, mock_budget_transactions):
        mock_create = MagicMock()
        mock_budget_transaction.return_value.create = mock_create

        self.assertEqual(mock_create.call_count, 1)
like image 36
tmarice Avatar answered Nov 15 '22 21:11

tmarice