/

September 26, 2023

Integration Testing with Django Rest Framework – Part 1

Integration Testing

This blog comprises of 3 parts:

  1. Part 1 – Fundamentals of Integration Testing with Django Rest Framework (This Blog)
  2. Part 2 – Django Token-Based Authentication Test Guidelines
  3. Part 3 – How to Mock Methods in Django Tests

We hope you find this blog useful. In this blog series, we will explore integration testing with the Django Rest Framework in detail, as the goal is to enhance your understanding of testing in Django and help you build robust applications.

Contact us here.

Introduction

In the world of web development, integration testing plays a vital role in ensuring the reliability and functionality of APIs. This article dives into integration testing with the Django Rest Framework, a powerful toolset for building RESTful APIs in Django.

Without proper integration testing, several issues can arise, impacting the performance, reliability, and security of Django Rest Framework APIs. Inconsistent data flow may lead to incorrect or incomplete data being transmitted through the APIs, while changes to API endpoints or data models can break compatibility with other services.

Performance bottlenecks may occur due to inefficient API implementations or unexpected interactions with external systems. To avoid all these things, integration testing is a must-have in any enterprise-level project.

Step-by-step Approach

The article begins by explaining the significance of testing API endpoints without authentication. It explores how to write integration tests for unauthenticated endpoints using the Django Rest Framework’s test client.

Part 1: Fundamentals of Integration Testing with Django Rest Framework

You must specify a class that derives from TestCase in order to construct a test case in your Django project.
The following techniques from TestCase will help us while we test:

  1. setUp: This function will be called before the execution of each unit test (methods in which you will be testing your Django views)
  2. setUpTestData: This function can be used to define data for the whole test case. That means that this function automatically rolls back the changes in the database after the finalisation of all the unit tests in the test case. This is mainly used to load data to your test database.
  3. RequestFactory: RequestFactory provides a way to generate a request instance that can be used as the first argument to any view. This means you can test a view function the same way as you would test any other function

Example

Let’s begin by configuring the fundamental CRUD APIs in Django for a model. The Employee model will be used as an example. The code for the model, serializer, and views is provided below:

Model

class Employee(models.Model):
   id = models.UUIDField(primary_key=True)
   name = models.CharField(max_length=100, null=False)
   designation = models.TextField(null=False)
   is_active = models.BooleanField(default=True)

 

Serializer

class EmployeeSerializer(serializers.ModelSerializer):
   class Meta:
   model = Employee
   fields = ['all']

 

Views

class EmployeeCollectionView(generics.ListCreateAPIView):
   name = 'employee-collection-view'
   queryset = Employee.objects.all()
   serializer_class = EmployeeSerializer

class EmployeeMemberView(generics.RetrieveUpdateDestroyAPIView):
   name = 'employee-member-view'
   queryset = Employee.objects.all()
   serializer_class = EmployeeSerializer

   def get_object(self):
       return Employee.objects.get(id=self.kwargs['pk'])

 

We are now ready to write integration tests as these elements are in place. We’ll begin with integration testing without authentication for the purposes of this article.

Tests

For each API endpoint, use RequestFactory to generate a request object. The setUp method initialises an instance of RequestFactory, which will be used to create the requests.

In each test method, create the appropriate request object using self.factory and then call the corresponding view using as_view(), passing the request object and any required parameters.

Use the assertions to validate the response status code and perform any necessary data validation.

Example

from django.test import TestCase, RequestFactory
## Import views/seralizers as needed

class EmployeeTest(TestCase):
    def setUp(self):
        self.factory = RequestFactory()

    @classmethod
    def setUpTestData(cls):
        cls.emp1 = Employee.objects.create(name='John', designation='CTO')
        cls.emp2 = Employee.objects.create(name='Roy', designation='CEO')

    def test_get(self):
        request = self.factory.get(BASE_URL)

        response = EmployeeCollectionView.as_view()(request)

        self.assertEqual(response.status_code, 200)
        self.assertEqual(len(response.data['results']), 2)

    def test_post(self):
        payload = {
            'name': 'Andy',
            'designation': 'Manager'
        }

        request = self.factory.post(BASE_URL, data=payload)

        response = EmployeeCollectionView.as_view()(request)

        self.assertEqual(response.status_code, 201)
        self.assertEqual(len(response.data['name']), payload['name'])
        self.assertEqual(len(response.data['designation']), payload['designation'])

    def test_retrieve(self):
        request = self.factory.get(f'{URL}/{self.emp1.id}')

        response = EmployeeMemberView.as_view()(request)

        self.assertEqual(response.status_code, 200)
        self.assertEqual(response.data['name'], self.emp1.name)
        self.assertEqual(response.data['designation'], self.emp1.designation)

    def test_delete(self):
        request = self.factory.delete(f'{URL}/{self.emp2.id}')

        response = EmployeeMemberView.as_view()(request)

        self.assertEqual(response.status_code, 200)

    def test_patch(self):
        payload = {
            'designation': 'Tech Head'
        }
        request = self.factory.patch(f'{URL}/{self.emp1.id}', data=payload)

        response = EmployeeMemberView.as_view()(request)

        self.assertEqual(response.status_code, 200)
        self.assertEqual(response.data['designation'], payload['designation'])

 

Conclusion

This blog covered the foundations of integration testing using the Django Rest Framework.

As we move forward in this series, we will delve deeper into integration testing, including testing authenticated endpoints, mocking responses, and ensuring robust API behaviour.

Author

Nikhil Chopdekar

Asso. Lead Engineer