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)