Skip to content Skip to sidebar Skip to footer

Mocking A RelatedManager In Django 2

This question is directly related to this question, but that one is now outdated it seems. I am trying to test a view without having to access the database. To do that I need to Mo

Solution 1:

You'll need to mock the return value of the create_reverse_many_to_one_manager factory function. Example:

def test_valid(mocker):
    mgr = mocker.MagicMock()
    mocker.patch(
        'django.db.models.fields.related_descriptors.create_reverse_many_to_one_manager', 
        return_value=mgr
    )

    user = user_factory.build()
    user.id = 1
    ...
    mgr.assert_called()

Beware that the above example will mock the rev manager for all models. If you need a more fine-grained approach (e.g. patch User.auth_token's rev manager only, leave the rest unpatched), provide a custom factory impl, e.g.

def test_valid(mocker):
    mgr = mocker.MagicMock()
    factory_orig = related_descriptors.create_reverse_many_to_one_manager
    def my_factory(superclass, rel):
        if rel.model == User and rel.name == 'auth_token_set':
            return mgr
        else:
            return factory_orig(superclass, rel)

    mocker.patch(
        'django.db.models.fields.related_descriptors.create_reverse_many_to_one_manager',
        my_factory
    )

    user = user_factory.build()
    user.id = 1
    ...
    mgr.assert_called()

Solution 2:

I accomplish this doing this(Django 1.11.5)

@patch("django.db.models.fields.related_descriptors.create_reverse_many_to_one_manager")
def test_reverse_mock_count(self, reverse_mock):
    instance = mommy.make(DjangoModel)

    manager_mock = MagicMock
    count_mock = MagicMock()
    manager_mock.count = count_mock()
    reverse_mock.return_value = manager_mock

    instance.related_manager.count()
    self.assertTrue(count_mock.called)

hope this help!


Solution 3:

If you use django's APITestCase, this becomes relatively simple.

class TestChangeEmail(APITestCase):
    def test_valid(self):
        user = UserFactory()
        auth_token = AuthToken.objects.create(user=user)

        response = self.client.post(
            reverse('your endpoint'), 
            data={'email': 'foo@example.com'}
        )

        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertFalse(AuthToken.objects.filter(user=user).exists())

This avoids mocking altogether and gives a more accurate representation of your logic.


Solution 4:

unittest.PropertyMock can be used to mock descriptors in a way that doesn't require mocking internal implementation details:

def test_valid(mocker, user_factory):
    user = user_factory.build()
    user.id = 1

    data = {
        'email': 'foo@example.com'
    }

    factory = APIRequestFactory()
    request = factory.post('/', data=data)
    force_authenticate(request, user)

    mocker.patch.object(user, "save")

    with mocker.patch('app.views.User.auth_token_set', new_callable=PropertyMock) as mock_auth_token_set:
        mock_delete = mocker.MagicMock()
        mock_auth_token_set.return_value.all.return_value.delete = mock_delete

        response = ChangeEmail.as_view()(request)

    assert response.status_code == status.HTTP_200_OK
    assert mock_delete.call_count == 1

Post a Comment for "Mocking A RelatedManager In Django 2"