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?
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
Related
I send a request to the site https://swapi.dev/ the received data needs to be sorted:
Provide the functionality to count the occurrences of values (combination of values) for columns.
For example when selecting the columns date and homeworld the table should show the
counts as follows:
my code is not very good because I write something like this for the first time:
import requests
from rest_framework.views import APIView
from rest_framework.viewsets import ModelViewSet
from rest_framework import status
from rest_framework.response import Response
from datetime import datetime
from rest_framework import filters
from rest_framework.pagination import PageNumberPagination
from rest_framework.decorators import action
# class StarWarsAPIListPagination(PageNumberPagination):
# page_size = 4
# page_size_query_param = 'page_size'
# max_page_size = 20
class StarView(APIView):
# pagination_class = StarWarsAPIListPagination
# filter_backends = (filters.OrderingFilter)
# ordering_fields = ('name',)
def get(self, request):
date = datetime.today()
page = request.query_params.get('page', 1)
url = f'https://swapi.dev/api/people/?page={page}'
response = requests.get(url, date).json()
return Response(response, status=status.HTTP_200_OK)
class StartDateView(APIView):
def get(self, request):
date = datetime.today()
page = request.query_params.get('page', 1)
url = f'https://swapi.dev/api/people/?page={page}'
response = requests.get(url).json()
a = response['results'][0]['name']
# a = response['results']
# for i in range(len(a)):
# if a[i]["name"] == "R2-D2":
# return Response(a[i]['name'], status=status.HTTP_200_OK)
I want to create a service using Django Rest API. I have a function. The result of this function should return 2 values and I should return these values in JSON API format.
The function will work like this. I will receive the features_list as a parameter and I will use it to create a result and display it as a service in json format in def prediction function.
I created a sample API (I guess) it is class PredictionSet in my views but I actually want to make service the def prediction function in my views.
I cannot understand how to apply it. I am so confused. Any help would be appreciated.
models.py
class Liquidity(models.Model):
pred_y = models.CharField(max_length=600)
score = models.FloatField()
views.py
class PredictionSet(viewsets.ModelViewSet):
queryset = Liquidity.objects.all()
serializer_class = LiquiditySerializer
def prediction(request, features_list):
filename = config.FINAL_MODEL_PATH
classifier = pickle.load(open(filename, 'rb'))
scale_file = config.SCALER_PATH
scaler = pickle.load(open(scale_file, 'rb'))
sample = np.array(features_list).reshape(1, -1)
sample_scaled = scaler.transform(sample)
pred_y = classifier.predict(sample_scaled)
prob_y = classifier.predict_proba(sample_scaled)
if prob_y[0][1] < 0.5:
score = 0
elif prob_y[0][1] <= 0.69:
score = 1
else:
score = 2
pred_y = pred_y[0]
prediction_obj = Liquidity.objects.get_or_create(pred_y=pred_y, score=score)
prediction_result = prediction_obj.pred_y
prediction_score = prediction_obj.score
context = {
'prediction_result ': prediction_result,
'prediction_score ': prediction_score,
}
return context
serializer.py
class LiquiditySerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Liquidity
fields = '__all__'
If you want to return custom JSON from a ModelViewset in DRF, you can override .list() and/or .retrieve() like this :
from rest_framework import status
from rest_framework.response import Response
class PredictionSet(viewsets.ModelViewSet):
queryset = Liquidity.objects.all()
serializer_class = LiquiditySerializer
# Your custom function definition
def prediction(self, request, features_list):
# The content
def retrieve(self, request, *args, **kwargs):
result = prediction(...) # Call your custom service and got result
# Return the result as JSON (url = /api/v1/predictions/1) an object
return Response({'data': result}, status=status.HTTP_200_OK)
def list(self, request, *args, **kwargs):
result = prediction(...) # Call your custom service and got result
# Return the result as JSON (url = /api/v1/predictions) a list of objects
return Response({'data': result}, status=status.HTTP_200_OK)
For more details follow this link
I would like to implement pagination in my custom jsonresponse function. But I have no idea on how would i implement this. This is the code of my function. Any inputs will be a great help. Thank you.
def json_response(data = {}, message = 'successful!', status = 'success', code = 200):
data_set = {}
status = 'success' if code == 200 else 'error'
if status == 'success':
data_set['code'] = code
data_set['status'] = status
data_set['message'] = message
# data_set['data'] = data.data
try:
data_set['data'] = data.data
except TypeError:
data_set['data'] = json.dumps(data)
except AttributeError:
data_set['data'] = data
else:
data_set['code'] = code
data_set['status'] = status
data_set['message'] = message
return JsonResponse(data_set, safe=False, status=code)
if you're using Django Rest Framework, checkout the Pagination provided by the framework
https://www.django-rest-framework.org/api-guide/pagination/
Example:
# pagination.py
from rest_framework.pagination import PageNumberPagination
class CustomNumberPagination(PageNumberPagination):
page_size = 10 # Put the number of items you desire
# views.py
from somewhere.pagination import CustomNumberPagination
from somewhere.model import SomeModel
from somewhere.serializer import SomeSerializer
from rest_framework.generics import ListAPIView
class SomeView(generics.ListAPIView):
queryset = SomeModel.objects.all()
serializer_class = SomeSerializer
pagination_class = CustomNumberPagination
P.S. Also it can be configured globally like below
# settings.py
REST_FRAMEWORK = {
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
'PAGE_SIZE': 10, # Put the number of items you desire
}
class logEngine:
def logger(request):
t1 = time.time()
params = {'user': request.user.username,
'ip_address': socket.gethostbyname(socket.gethostname()),
'process_time': time.time() - t1,
}
return params
when I uses request.user.username (which is given in most of the examples in the internet), I get the below error,
AttributeError: 'logEngine' object has no attribute 'user'
Please suggest any other method
I got the solution. It's working fine now
customdblogger_views.py
import socket
import time
import logging
from urllib import request
class logEngine:
def logger(self, request):
t1 = time.time()
params = {'user': request.user.username,
'ip_address': socket.gethostbyname(socket.gethostname()),
'process_time': time.time() - t1,
}
return params
def django_logger(self):
django_logger = logging.getLogger('db')
return django_logger
views.py
# Create your views here.
import logging
import sys
from rest_framework import status
from rest_framework.decorators import api_view
from rest_framework.response import Response
from rest_framework.views import APIView
from CustomDBLogger.customdblogger_views import logEngine
from rest_framework import viewsets, status
from rest_framework.decorators import action
db_logger = logging.getLogger('db')
class Request(viewsets.GenericViewSet):
attributes = logEngine()
#action(detail=True)
def test_api_method(self, request):
params2 = {
'class_name': __class__.__name__,
'method_name': sys._getframe().f_code.co_name,
'module_name': __name__,
}
v1 = self.attributes.logger(request)
params2.update(dict(v1))
db_logger.info("Successfully entered into function", extra=params2)
try:
1 / 0
except Exception as e:
db_logger.exception(e, extra=params2)
return Response({'data': True}, status=status.HTTP_200_OK)
finally:
db_logger.info("exited from the function", extra=params2)
Upon implementing a throttle for a REST API, I'm encountering an issue when running my tests all at once.
Upon isolating the subject TestCase and running the test runner, the TestCase passes its assertions. However when all the tests are ran I get the following error: AssertionError: 429 != 400. Which that type of error of course is due to the requests exceeding a rate limit.
How can I disable throttling for the tests so the assertion error is not raised. I decorated the TestCase with #override_settings but that doesn't have any effect.
from copy import deepcopy
from django.conf import settings
from django.test import TestCase, override_settings
from django.contrib.auth.models import User
from rest_framework.test import APITestCase, APIClient
from django.urls import reverse
from ..models import QuestionVote, Question
from users.models import UserAccount
from tags.models import Tag
from .model_test_data import mock_questions_submitted
REST_FRAMEWORK = deepcopy(settings.REST_FRAMEWORK)
del REST_FRAMEWORK['DEFAULT_THROTTLE_RATES']
#override_settings(REST_FRAMEWORK=REST_FRAMEWORK)
class TestUserVoteOnOwnQuestion(APITestCase):
'''Verify that a User cannot vote on their own Question'''
#classmethod
def setUpTestData(cls):
cls.user1 = User.objects.create_user("Me", password="topsecretcode")
cls.user1_account = UserAccount.objects.create(user=cls.user1)
cls.tag = Tag.objects.create(name="Tag")
cls.q = mock_questions_submitted[2]
cls.q.update({'user_account': cls.user1_account})
cls.question = Question(**cls.q)
cls.question.save()
cls.question.tags.add(cls.tag)
def test_vote_on_own_posted_question(self):
self.client.login(username="Me", password="topsecretcode")
response = self.client.put(
reverse("questions_api:vote", kwargs={'id': 1}),
data={"vote": "upvote"}
)
self.assertEqual(response.status_code, 400)
self.assertEquals(
response.data['vote'],
"Cannot vote on your own question"
)
REST_FRAMEWORK = {
'TEST_REQUEST_DEFAULT_FORMAT': 'json',
'DEFAULT_THROTTLE_RATES': {
'voting': '5/minute'
}
}
class UserQuestionVoteView(APIView):
renderer_classes = [JSONRenderer, ]
parser_classes = [JSONParser, ]
permission_classes = [IsAuthenticated, ]
authentication_classes = [SessionAuthentication, ]
throttle_classes = [ScopedRateThrottle, ]
throttle_scope = "voting"
def put(self, request, id):
# import pdb; pdb.set_trace()
account = UserAccount.objects.get(user=request.user)
question = Question.objects.get(id=id)
if account == question.user_account:
return Response(data={
'vote': "Cannot vote on your own question"
}, status=400)
try:
stored_vote = QuestionVote.objects.get(
account=account, question=question
)
serializer = QuestionVoteSerializer(stored_vote, request.data)
except QuestionVote.DoesNotExist:
serializer = QuestionVoteSerializer(data=request.data)
finally:
if serializer.is_valid(raise_exception=True):
question_vote = serializer.save(
account=account,
question=question
)
vote = serializer.validated_data['vote']
if vote == "downvote":
question.vote_tally = F('vote_tally') - 1
else:
question.vote_tally = F('vote_tally') + 1
question.save()
question.refresh_from_db()
return Response(data={
'id': question.id,
'tally': question.vote_tally
})
return Response(serializer.errors)
One way to do this is by setting your config files up to support testing versions:
# config.py
REST_FRAMEWORK = {
'TEST_REQUEST_DEFAULT_FORMAT': 'json',
'DEFAULT_THROTTLE_RATES': {
'voting': '5/minute'
}
}
TESTING = len(sys.argv) > 1 and sys.argv[1] == 'test'
if TESTING:
del REST_FRAMEWORK['DEFAULT_THROTTLE_RATES']
The pro of this approach is you're not hacking away at your application in tests and hiding modifications to the config file - all of your testing based changes are in the same config file as their true values.
The con of this approach is unless your developers know this setting and values are there, they may be scratching their heads as to why the throttling doesn't work in tests, but does at runtime.
My solution was to apply some monkey patching.
I have a throttles.py file where I have custom throttles, such as
class UserBurstRateThrottle(UserRateThrottle):
rate = '120/minute'
What I've done is create a stub allow_request function to always return true, so something like
def apply_monkey_patching_for_test():
def _allow_request(self, request, view):
return True
UserBurstRateThrottle.allow_request = _allow_request
Then, in the test_whatever.py file, I add the following at the top.
from my_proj import throttles
throttles.apply_monkey_patching_for_test()
Another easy way is to disable the cache that's responsible for storing the clients' meta data (https://www.django-rest-framework.org/api-guide/throttling/#setting-up-the-cache). So you need this in your test settings:
CACHES = {
'<throttling-cache-name|default>': {
'BACKEND': 'django.core.cache.backends.dummy.DummyCache', # to prevent API throttling
}
}