Testing Django Rest Framework #DRF endpoints can sometimes be a complex and time-consuming process. However, the #drf-api-action Python package aims to simplify and enhance the testing experience by introducing a custom decorator, api-action.
In this article, we will explore how to leverage #drf-api-action to streamline testing in DRF, making the development process more time-efficient and enjoyable.
Installation:
Let's start by installing the #drf-api-action package using pip:
pip install drf-api-action
Usage:
Now that we have the package installed, let's dive into how
#drf-api-action can significantly improve testing workflows in #DRF.
Add the package to an existing project:
we have a User model which exposes 2 endpoints:
- /api/users/{id} : A GET request that returns details on a specific user.
- /api/users/ : A POST request that creates a new user.
from rest_framework import mixins, viewsets, status
from rest_framework.decorators import action
from rest_framework.response import Response
from drf_api_action_example.users.models import User
from drf_api_action_example.users import serializers
class UsersViewSet(mixins.RetrieveModelMixin, viewsets.GenericViewSet):
@action(detail=True,
methods=['get'],
url_path='/',
url_name='users/',
serializer_class=serializers.GetUserDetailsSerializer)
def get_user_details(self, request, **kwargs):
"""
returns user details, pk expected
"""
serializer = self.get_serializer(instance=self.get_object())
return Response(data=serializer.data, status=status.HTTP_200_OK)
@action(detail=False,
methods=['post'],
url_path='/',
url_name='users/',
serializer_class=serializers.AddUserSerializer)
def add_user(self, request, **kwargs):
"""
adds a new user
"""
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
serializer.save()
return Response(data=serializer.data, status=status.HTTP_201_CREATED)
Now, to apply the benefits of #drf-api-action we will change the following:
from rest_framework import mixins, viewsets, status
# instead of from rest_framework.decorators import action do:
from drf_api_action.mixins import APIRestMixin
from drf_api_action.decorators import action_api
from rest_framework.response import Response
from drf_api_action_example.users.models import User
from drf_api_action_example.users import serializers
# inheriting APIRestMixin
class UsersViewSet(APIRestMixin, mixins.RetrieveModelMixin, viewsets.GenericViewSet):
# replacing @action decorator
@action_api(detail=True,
methods=['get'],
url_path='/',
url_name='users/',
serializer_class=serializers.GetUserDetailsSerializer)
def get_user_details(self, request, **kwargs):
"""
returns user details, pk expected
"""
serializer = self.get_serializer(instance=self.get_object())
return Response(data=serializer.data, status=status.HTTP_200_OK)
# replacing @action decorator
@action_api(detail=False,
methods=['post'],
url_path='/',
url_name='users/',
serializer_class=serializers.AddUserSerializer)
def add_user(self, request, **kwargs):
"""
adds a new user
"""
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
serializer.save()
return Response(data=serializer.data, status=status.HTTP_201_CREATED)
Fun Part - Writing tests!
First, import:
import pytest
from users.models import User
from users.views import UsersViewSet
from rest_framework.exceptions import ValidationError
The first test test_get_user_detais
simply tests /api/users/{id} GET endpoint:
import pytest
from users.models import User
from users.views import UsersViewSet
from rest_framework.exceptions import ValidationError
users_api = UsersViewSet()
def test_get_user_details(users_api):
user = User(first_name='shosho', last_name='bobo', age=30)
user.save()
user_details = users_api.get_user_details(pk=user.id)
assert user_details['first_name'] == user.first_name
- The second test
test_add_user
simply tests /api/users POST endpoint:
def test_add_user(users_api):
output = users_api.add_user(first_name='bar', last_name='baz', age=30)
assert output['id'] is not None
- The third test
test_add_user_exception_on_age
simply tests /api/users POST endpoint when age is not in the valid range:
def test_add_user_exception_on_age(users_api):
with pytest.raises(ValidationError) as error:
users_api.add_user(first_name='bar', last_name='baz', age=150)
assert "Ensure this value is less than or equal to 120" in str(error.value)
Unlike traditional #testing methods, #drf-api-action provides clear tracebacks, making it easier to identify and fix errors in your code.
Demo
Conclusion
The #drf-api-action package is a game-changer for testing #DRF/#Django endpoints in #Python. By simplifying the testing process and providing clear tracebacks and pagination support, it allows developers to focus on writing robust and time-efficient code.
With #drf-api-action, testing in #DRF becomes more intuitive and enjoyable, contributing to an overall improved development experience.
Start leveraging this powerful package in your #DRF projects today!
P.S if you liked it, star it on #GitHub 💫
Resources
full example project
drf-api-action
source code
django-rest-framework
documentation
Top comments (0)