Testing with Django REST Framework
Tags: python, django, testing, opensource
Published: 2022-07-19 12:05:33

---
title: Testing with Django REST Framework 
published: true
description: Django REST Framework has great testing tools included. In this article, I will show you how I use them with other great libraries to test my API Views.
tags: python, django, testing, opensource 
cover_image: https://www.django-rest-framework.org/img/logo.png
# Use a ratio of 100:42 for best results.
---

>_It’s not because things are difficult that we dare not venture. It’s because we dare not venture that they are difficult._ - Seneca



A section of Django REST framework (DRF) [documentation](https://www.django-rest-framework.org/api-guide/testing) is dedicated to testing your API views. It has a lot of information about the different tools the library is providing and code examples on how to use them. I was asked how to test an API call by mimicking a request, so here is how I usually do it in my projects.

## The serializer

First, we have a basic model for a home which fields describe its address. `BaseModel` is an abstract class that adds a `UUID`, a `created_at` and a `modified_at` fields to my objects:

```Python
class Home(BaseModel):
    class SupportedCountries:
        CAN = "CANADA"
        US = "UNITED_STATES"

        CHOICES = (
            (CAN, _("Canada")),
            (US, _("United States")),
        )

    name = models.CharField(max_length=50, default="Home")
    address_line_1 = models.CharField(max_length=500)
    address_line_2 = models.CharField(
        max_length=500, blank=True, verbose_name=_("Address line 2 (optional)")
    )
    city = models.CharField(max_length=100)
    state_province = models.CharField(max_length=50, verbose_name=_("State/Province"))
    zip_code_postal_code = models.CharField(
        max_length=7, verbose_name=_("Zip code/Postal code")
    )
    country = models.CharField(
        max_length=15,
        choices=SupportedCountries.CHOICES,
        default=SupportedCountries.US,
    )

    def __str__(self):
        return self.name
```

The resulting serializer is thus very simple with no inner serializer. We declare all the fields from the model plus the `id` field from the `BaseModel` abstract class:

```Python
class HomeSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.Home
        fields = [
            "id",
            "name",
            "address_line_1",
            "address_line_2",
            "city",
            "state_province",
            "zip_code_postal_code",
            "country",
        ]
```

## The test class

To generate objects based on my models for my tests, I use [factory boy](https://factoryboy.readthedocs.io/en/stable/orms.html) and I populate the fields with fake data using [faker](https://faker.readthedocs.io/en/master/):

```Python
import factory
from factory.django import DjangoModelFactory


class HomeFactory(DjangoModelFactory):
    name = "Home"
    address_line_1 = factory.Faker("street_address")
    address_line_2 = factory.Faker("building_number")
    city = factory.Faker("city")
    state_province = factory.Faker("state")
    zip_code_postal_code = factory.Faker("postcode")
    country = models.Home.SupportedCountries.US

    class Meta:
        model = models.Home
```

I usually use [Django TestCase](https://docs.djangoproject.com/en/4.0/topics/testing/overview/) class for testing purposes, which is a subclass of Python unittest.TestCase. It has a client to authenticate a user so you could test mixins and permission behaviors. DRF has a class called [APITestCase](https://www.django-rest-framework.org/api-guide/testing/#api-test-cases) that extends Django `TestCase` with the same functions but using [APIClient](https://www.django-rest-framework.org/api-guide/testing/#apiclient) which allows one to authenticate as an API user.

```Python
class TestHomeAPIView(APITestCase):
    def setUp(self):
        self.url = reverse("api:home_view") # use the view url
        self.home = factories.HomeFactory()
        user = factories.UserFactory()
        self.client.force_authenticate(user=user)

    def test_get(self):
        response = self.client.get(self.url)
        response.render()
        self.assertEquals(200, response.status_code)
        expected_content = {
                "id": str(self.home.id),
                "address_line_1": self.home.address_line_1,
                "address_line_2": self.home.address_line_2,
                "city": str(self.home.city),
                "state_province": str(self.home.state_province),
                "zip_code_postal_code": str(self.home.zip_code_postal_code),
                "country": str(self.home.country),
            }
        self.assertListEqual(expected_content, json.loads(response.content))
```

Let's dive into that test code and explain what it does. First we have the `setUp` function which is common to TestCase and define code that will be run before every function prefixed with `test_` in the class. I define the url for my view and a `home` object that will be recreated each time, making my test deterministic. I then create a user with a factory that gives the permissions I need for testing the view. The last line is really important, even more so for an API view with [authentication](https://www.django-rest-framework.org/api-guide/authentication/) and [permission](https://www.django-rest-framework.org/api-guide/permissions/) mechanisms. By default, the `DEFAULT_PERMISSION_CLASSES` array contains `rest_framework.permissions.IsAuthenticated` so all my views require to be logged in as a user. We authenticate the user with the `APIClient` so we can make calls to the view and the permissions can be evaluated.

I then declare `test_get` to make a simple `GET` test of my view. I first capture the call response into a variable of the same name. At first, I assumed `response.content` would be available, but because of template rendering mechanism internal to Django, it is not. We need to manually declare the rendering. Then I assert that my response was successful and generated an [HTTP 200](https://http.cat/200) that means everything went fine. The next line declares an `expected_content` variable which is dict containing all the fields of my `home` object. Lastly, I parse a JSON object to a Python object using `json.loads`, which I compare to my expected content to make sure what I received is correct.

## Wrapping up

Django REST Framework has a great documentation with a thorough section on tests. We went through some bits of code for the model, the serializer and finally the test. This test was written à la Django using factory boy and Faker, and leveraging DRF `APIClient` contained in `APITestCase` testing class.

We went from start to finish of my testing process for an API view. There might be some portions that could be optimized, but it proved to be reliable for me.

Please share with me any tips or amazing bits of code that make your testing experience with Django REST Framework easier. If you spot an error, do not hesitate to leave a comment.