Search code examples
pythondjangodjango-rest-frameworkpython-mockdjango-unittest

Mocking patched class methods is not working


I'm using Django with DRF and python mock. What I am trying to do is to test my views and mock a serializer and some methods.

This is what I have:

views.py

from gmm_mobile.v1.serializers import RegisterParticipationSerializer
from gmm_mobile.v1.exceptions import GameOrCampaignDoesNotExist
from gmm_util.header import Header
from gmm_util.response import ResponseUtils
from gmm_util.permissions import MobileTokenPermission
from gmm_util.permissions import MobileTokenAuthentication

class ParticipantViewMobile(APIView):
    permission_classes = (MobileTokenPermission, )
    authentication_classes = (MobileTokenAuthentication, )

    def post(self, request, service_id, campaign_id):
        try:
            environment_info = Header.get_environment_info(request)

            request.data[Field.GAME_SERVICE_ID] = service_id
            request.data[Field.CAMPAIGN] = campaign_id
            request.data[Field.LOCATION] = environment_info

            participation_serializer = RegisterParticipationSerializer(data=request.data)
            participation_serializer.is_valid(raise_exception=True)
            participation_serializer.save()

            return ResponseUtils.created()
        except Http404:
            raise GameOrCampaignDoesNotExist()

serializers.py

class RegisterParticipationSerializer(serializers.ModelSerializer):
    location = LocationSerializer(many=False)
    campaign = serializers.IntegerField()
    game_service_id = serializers.CharField(max_length=254)

    class Meta:
        model = Participation
        fields = (Field.PK, Field.EMAIL, Field.GPG_ID, Field.DATE, Field.LOCATION, Field.INFO, Field.CAMPAIGN,
                  Field.GAME_SERVICE_ID, Field.GCM_ID)
        read_only_fields = (Field.GCM_ID,)

test_views.py

from mock import patch
from django.core.urlresolvers import reverse
from rest_framework.test import APITestCase, APIRequestFactory
from rest_framework import status
from rest_framework.exceptions import ValidationError

from gmm_mobile.v1.views import ParticipantViewMobile
from gmm_mobile.v1.urls import CONSULT_PARTICIPANT
from gmm_push.environment_configuration import EnvironmentConfiguration
from gmm_util.util_test import JsonObjects

class ParticipantViewMobileTests(APITestCase):

    factory = APIRequestFactory()
    url = reverse(PARTICIPANT_MOBILE, kwargs={'service_id': 1, 'campaign_id': 1})

    def setUp(self):
        self.view = ParticipantViewMobile.as_view()

    @patch('gmm_mobile.v1.views.RegisterParticipationSerializer')
    def test__post__invalid_data__status_400(self, mock_serializer):
        # Arrange
        mock_serializer.is_valid.side_effect = ValidationError({})
        request = self.factory.post(
            self.url,
            {},
            HTTP_X_ENV=JsonObjects.environment_info_1(),
            HTTP_X_TOKEN=EnvironmentConfiguration.get_token(False),
            format='json')
        # Act
        response = self.view(request, 1, 1)
        # Assert
        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)

But when I run the test, the side_effect is not working. When I change the .is_valid in the test it has no effect on ParticipantViewMobile.post.

If I do @patch('gmm_mobile.v1.views.RegisterParticipationSerializer.is_valid') and mock_serializer.side_effect = ValidationError({}) it works, but I don't want that because there will be cases that I will need to mock more than one method, like .save and .is_valid.

I also tried to change the import styles views.py and test_views.py but it still didn't work.

Am I doind something wrong or missing anything?

EDIT 1:

I've put prints on the view to try to understand what was happening:

def post(self, request, service_id, campaign_id):
    try:
        environment_info = Header.get_environment_info(request)

        request.data[Field.GAME_SERVICE_ID] = service_id
        request.data[Field.CAMPAIGN] = campaign_id
        request.data[Field.LOCATION] = environment_info

        participation_serializer = RegisterParticipationSerializer(data=request.data)
        print RegisterParticipationSerializer.is_valid.side_effect
        print participation_serializer.is_valid.side_effect
        participation_serializer.is_valid(raise_exception=True)
        participation_serializer.save()

        return ResponseUtils.created()
    except Http404:
        raise GameOrCampaignDoesNotExist()

And the output:

{}
None

So, when I create an instance of RegisterParticipationSerializer, I lost the mocked methods. How to avoid this?


Solution

  • Your mock doesn't work because you're mocking the class and not the instance being generated from the class. Try this:

    mock_instance = Mock()
    mock_instance.is_valid.side_effect = ValidationError({})
    mock_serializer.return_value = mock_instance