Simplifying django-rest-framework Testing with drf-api-action
Tags: django, testing, python, opensource
Published: 2023-12-17 15:00:23

---
title: Simplifying django-rest-framework Testing with drf-api-action
published: true
description: we will explore how to use drf-api-action to streamline testing in DRF, making the development process more time-efficient and enjoyable
meta: drf-api-action
tags: #Django #testing #python #opensource
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/livxkq105ra5pcxguake.png
# Use a ratio of 100:42 for best results.
# published_at: 2023-12-17 14:44 +0000
---

---

Testing Django Rest Framework [#DRF](https://dev.to/t/drf) endpoints can sometimes be a complex and time-consuming process. However, the [#drf-api-action](https://dev.to/t/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](https://dev.to/t/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](https://dev.to/t/drf-api-action) package using pip:
```bash
pip install drf-api-action
```
---

##Usage:
Now that we have the package installed, let's dive into how
[#drf-api-action](https://dev.to/t/drf-api-action) can significantly improve testing workflows in [#DRF](https://dev.to/t/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.

```python
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](https://dev.to/t/drf-api-action) we will change the following:

```python
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:

```python
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:

```python
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
```

2. The second test `test_add_user` simply tests /api/users POST endpoint:
```python
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
```

3. The third test `test_add_user_exception_on_age` simply tests /api/users POST endpoint when age is not in the valid range:

```python
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](https://dev.to/t/testing) methods, [#drf-api-action](https://dev.to/t/drf-api-action) provides clear tracebacks, making it easier to identify and fix errors in your code.

---
## Demo
<img width="100%" style="width:100%" src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ofotb8yiu9f6e8wjoqm0.gif">

---

##Conclusion
The [#drf-api-action](https://dev.to/t/drf-api-action) package is a game-changer for testing [#DRF](https://dev.to/t/drf)/[#Django](https://dev.to/t/django) endpoints in [#Python](https://dev.to/t/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](https://dev.to/t/drf-api-action), testing in [#DRF](https://dev.to/t/drf) becomes more intuitive and enjoyable, contributing to an overall improved development experience. 
Start leveraging this powerful package in your [#DRF](https://dev.to/t/drf) projects today!

P.S if you liked it, star it  on [#GitHub](https://dev.to/t/github) :dizzy: 

---

##Resources

[full example project](https://github.com/Ori-Roza/drf-api-action-example)
`drf-api-action` [source code](https://github.com/Ori-Roza/drf-api-action)
`django-rest-framework` [documentation](https://www.django-rest-framework.org/tutorial/quickstart/)