Paramclasses Academy - Blog

Tutorials Details

←
Post 26 of 29
→

Django REST Framework API Testing and Documentation – Automated Testing, CRUD Endpoint Validation, Swagger UI, and OpenAPI

Django REST Framework API Testing and Documentation – Automated Testing, CRUD Endpoint Validation, Swagger UI, and OpenAPI

October 21, 2026

Django REST Framework API Testing and Documentation: Automated Testing, CRUD Endpoint Validation, Swagger UI, and OpenAPI

Introduction

Django REST Framework (DRF) provides powerful tools for building RESTful APIs. However, creating API endpoints is only one part of API development. Developers must also verify that endpoints work correctly, validate responses, test authentication and permissions, and provide clear documentation for other developers.

API testing helps identify bugs before an application goes into production. API documentation explains available endpoints, request parameters, authentication requirements, request bodies, and response formats.

In this module, you will learn how to test Django REST Framework APIs using Django's testing tools, validate CRUD operations, and generate interactive API documentation using Swagger UI and OpenAPI.

What You Will Learn

  • What API testing is and why it is important.

  • How to write automated tests for Django REST Framework APIs.

  • How to test GET, POST, PUT, PATCH, and DELETE requests.

  • How to verify HTTP status codes and JSON responses.

  • How to test authentication and permissions.

  • How to use APITestCase and APIClient.

  • How to generate API documentation using Swagger UI and OpenAPI.

  • How to run tests and interpret test results.

1. Understanding API Testing

API testing verifies whether an API behaves according to its requirements. Instead of testing only the visual interface, developers send HTTP requests directly to endpoints and inspect their responses.

For example, a product API might provide the following endpoints:

HTTP Method Endpoint Purpose
GET /api/products/ Retrieve all products
GET /api/products/1/ Retrieve one product
POST /api/products/ Create a product
PUT /api/products/1/ Replace a product
PATCH /api/products/1/ Update selected fields
DELETE /api/products/1/ Delete a product

A good API test checks more than whether a request succeeds. It can verify the response status, response data, database changes, validation errors, and access permissions.

2. Important Tools for API Testing

Django REST Framework includes tools that simplify automated API testing.

APITestCase provides a test case class designed for API requests.

APIClient simulates HTTP requests without requiring a running external server.

assertEqual() compares expected and actual values.

assertIn() verifies that a value exists in a collection.

response.status_code provides the HTTP status code returned by the endpoint.

For example:

from rest_framework.test import APITestCase
from rest_framework import status


class ProductAPITest(APITestCase):

    def test_example_response(self):
        response = self.client.get("/api/products/")

        self.assertEqual(
            response.status_code,
            status.HTTP_200_OK
        )

This test sends a GET request to the product endpoint and checks whether the server returns HTTP 200 OK.

Important: The endpoint must exist and be configured in your project's URL patterns for this test to pass.

3. Preparing a Product API for Testing

Assume your Django application contains a Product model.

models.py

from django.db import models


class Product(models.Model):
    name = models.CharField(max_length=100)
    price = models.DecimalField(
        max_digits=10,
        decimal_places=2
    )
    stock = models.PositiveIntegerField(default=0)

    def __str__(self):
        return self.name

Create a serializer to convert model instances into API representations.

serializers.py

from rest_framework import serializers
from .models import Product


class ProductSerializer(serializers.ModelSerializer):

    class Meta:
        model = Product
        fields = ["id", "name", "price", "stock"]

Create a viewset for CRUD operations.

views.py

from rest_framework.viewsets import ModelViewSet
from .models import Product
from .serializers import ProductSerializer


class ProductViewSet(ModelViewSet):
    queryset = Product.objects.all()
    serializer_class = ProductSerializer

Register the viewset using a router.

urls.py

from django.urls import path, include
from rest_framework.routers import DefaultRouter
from .views import ProductViewSet

router = DefaultRouter()
router.register(
    "products",
    ProductViewSet,
    basename="product"
)

urlpatterns = [
    path("api/", include(router.urls)),
]

The router automatically creates routes for listing, retrieving, creating, updating, and deleting products.

Ensure that the model migrations have been applied before running the tests.

4. Writing Your First Automated API Test

Create a tests.py file inside your Django application.

tests.py

from rest_framework import status
from rest_framework.test import APITestCase
from .models import Product


class ProductAPITest(APITestCase):

    def setUp(self):
        self.product = Product.objects.create(
            name="Keyboard",
            price="1500.00",
            stock=10
        )

        self.list_url = "/api/products/"
        self.detail_url = (
            f"/api/products/{self.product.id}/"
        )

    def test_get_product_list(self):
        response = self.client.get(self.list_url)

        self.assertEqual(
            response.status_code,
            status.HTTP_200_OK
        )

        self.assertEqual(len(response.data), 1)
        self.assertEqual(
            response.data[0]["name"],
            "Keyboard"
        )

Code Explanation

  • setUp() creates test data before each test method runs.

  • self.client.get() sends a GET request to the API.

  • response.status_code checks the HTTP response status.

  • response.data provides the parsed response content.

  • assertEqual() checks whether the returned data matches the expected result.

Django creates an isolated test database for its test suite, helping prevent test data from modifying your normal development database.

5. Testing the Create Endpoint (POST)

A POST request creates a new product.

Add the following test method to ProductAPITest:

def test_create_product(self):
    payload = {
        "name": "Mouse",
        "price": "700.00",
        "stock": 15
    }

    response = self.client.post(
        self.list_url,
        payload,
        format="json"
    )

    self.assertEqual(
        response.status_code,
        status.HTTP_201_CREATED
    )

    self.assertEqual(
        response.data["name"],
        "Mouse"
    )

    self.assertTrue(
        Product.objects.filter(name="Mouse").exists()
    )

What Does This Test Verify?

  1. The API accepts valid product data.

  2. The response returns HTTP 201 Created.

  3. The response contains the correct product name.

  4. The product is saved in the database.

A test like this verifies both the API response and the actual database operation.

6. Testing PUT and PATCH Endpoints

PUT generally replaces the resource representation, while PATCH updates selected fields.

Testing PUT

def test_update_product(self):
    payload = {
        "name": "Mechanical Keyboard",
        "price": "2500.00",
        "stock": 8
    }

    response = self.client.put(
        self.detail_url,
        payload,
        format="json"
    )

    self.assertEqual(
        response.status_code,
        status.HTTP_200_OK
    )

    self.product.refresh_from_db()

    self.assertEqual(
        self.product.name,
        "Mechanical Keyboard"
    )

Testing PATCH

def test_partial_update_product(self):
    response = self.client.patch(
        self.detail_url,
        {"stock": 20},
        format="json"
    )

    self.assertEqual(
        response.status_code,
        status.HTTP_200_OK
    )

    self.product.refresh_from_db()

    self.assertEqual(self.product.stock, 20)
    self.assertEqual(self.product.name, "Keyboard")

The PATCH test verifies that the stock changes while the product name remains unchanged.

7. Testing the Delete Endpoint

The DELETE request removes a product.

def test_delete_product(self):
    response = self.client.delete(self.detail_url)

    self.assertEqual(
        response.status_code,
        status.HTTP_204_NO_CONTENT
    )

    self.assertFalse(
        Product.objects.filter(
            id=self.product.id
        ).exists()
    )

HTTP 204 No Content is a common successful response for a DELETE operation in Django REST Framework's standard model viewsets.

The test also confirms that the product no longer exists in the database.

8. Testing Validation Errors

APIs must reject invalid input and return understandable error responses.

For example, if name is required, test an empty product name:

def test_create_product_with_empty_name(self):
    payload = {
        "name": "",
        "price": "500.00",
        "stock": 5
    }

    response = self.client.post(
        self.list_url,
        payload,
        format="json"
    )

    self.assertEqual(
        response.status_code,
        status.HTTP_400_BAD_REQUEST
    )

    self.assertIn("name", response.data)

This test expects the serializer to reject an empty name and return HTTP 400 Bad Request.

The exact validation behavior depends on the serializer and model configuration. If your project allows blank names, add the appropriate serializer validation before expecting this test to pass.

9. Testing Authentication and Permissions

Some APIs are public, while others require a logged-in user or a specific permission.

For authenticated API tests, DRF provides force_authenticate().

Example:

from django.contrib.auth import get_user_model
from rest_framework.test import APITestCase
from rest_framework import status


User = get_user_model()


class AuthenticatedAPITest(APITestCase):

    def setUp(self):
        self.user = User.objects.create_user(
            username="testuser",
            password="test-password-123"
        )

    def test_authenticated_request(self):
        self.client.force_authenticate(
            user=self.user
        )

        response = self.client.get("/api/products/")

        self.assertEqual(
            response.status_code,
            status.HTTP_200_OK
        )

This example assumes that the product list endpoint permits authenticated users and that its configured permissions allow this user to access it.

Important distinction: force_authenticate() is useful for testing permission-dependent views, but it bypasses the normal authentication mechanism. To test a real token or session login flow, send credentials using the appropriate authentication method instead.

A permission test should also check that unauthenticated or unauthorized users receive the expected response, such as HTTP 401 Unauthorized or HTTP 403 Forbidden, depending on the authentication and permission configuration.

10. Running Automated Tests

Open the terminal in your Django project directory, where manage.py is located.

Run all project tests:

python manage.py test

Run tests for one application:

python manage.py test products

Run one specific test class:

python manage.py test products.tests.ProductAPITest

If your application is named products, Django discovers the test methods whose names begin with test_.

A successful test run indicates that the executed tests passed. It does not guarantee that every possible application scenario is correct; comprehensive testing requires sufficient test coverage.

11. Understanding API Documentation

API documentation describes how other developers can interact with your API.

Good documentation commonly includes:

  • Available endpoints and HTTP methods.

  • Required URL parameters and query parameters.

  • Request body schemas.

  • Example requests and responses.

  • Response status codes.

  • Authentication requirements.

  • Validation errors and permission restrictions.

OpenAPI is a specification for describing HTTP APIs in a structured format. Tools can use an OpenAPI schema to generate interactive documentation, client libraries, and other developer resources.

Swagger UI presents API documentation in an interactive web interface where developers can inspect endpoints and, when configured and authorized, try requests.

12. Installing API Documentation Tools

A common option for Django REST Framework projects is drf-spectacular.

Install it in your active Python environment:

pip install drf-spectacular

Add the application to INSTALLED_APPS in settings.py:

INSTALLED_APPS = [
    # Existing Django applications...
    "rest_framework",
    "drf_spectacular",
]

Configure DRF to use the OpenAPI schema generator:

REST_FRAMEWORK = {
    "DEFAULT_SCHEMA_CLASS":
        "drf_spectacular.openapi.AutoSchema",
}

If your project already has a REST_FRAMEWORK dictionary, add or merge the setting instead of replacing your existing authentication, pagination, permission, or filtering configuration.

13. Configuring OpenAPI and Swagger UI

In your project's main urls.py, add the schema and documentation routes.

from django.contrib import admin
from django.urls import path, include
from drf_spectacular.views import (
    SpectacularAPIView,
    SpectacularSwaggerView,
    SpectacularRedocView,
)

urlpatterns = [
    path("admin/", admin.site.urls),

    path("", include("products.urls")),

    path(
        "api/schema/",
        SpectacularAPIView.as_view(),
        name="schema"
    ),

    path(
        "api/docs/",
        SpectacularSwaggerView.as_view(
            url_name="schema"
        ),
        name="swagger-ui"
    ),

    path(
        "api/redoc/",
        SpectacularRedocView.as_view(
            url_name="schema"
        ),
        name="redoc"
    ),
]

This configuration exposes three documentation-related endpoints:

URL Purpose
/api/schema/ Serves the generated OpenAPI schema
/api/docs/ Displays interactive Swagger UI
/api/redoc/ Displays API documentation using ReDoc

The schema endpoint normally serves a machine-readable representation of the API, commonly in YAML or JSON depending on the request and configuration.

Start the development server:

python manage.py runserver

Open the following address in your browser:

http://127.0.0.1:8000/api/docs/

You should see the interactive documentation for the API endpoints recognized by the schema generator.

Security note: Review whether API documentation should be publicly available in production. Internal APIs may require documentation access restrictions.

14. Improving Documentation with Schema Annotations

You can add descriptions and examples to make your API documentation clearer.

Example using extend_schema:

from drf_spectacular.utils import extend_schema
from rest_framework.viewsets import ModelViewSet

from .models import Product
from .serializers import ProductSerializer


@extend_schema(
    description="Create, retrieve, update, and delete products.",
    tags=["Products"],
)
class ProductViewSet(ModelViewSet):
    queryset = Product.objects.all()
    serializer_class = ProductSerializer

The tags setting groups related endpoints under the Products section in Swagger UI.

You can also annotate individual API actions when additional detail is needed. Refer to the drf-spectacular documentation for supported schema annotations and examples.

15. Best Practices for API Testing and Documentation

Follow these practices when building professional APIs:

  1. Test every CRUD operation instead of testing only GET requests.

  2. Test invalid input and confirm that validation errors are handled correctly.

  3. Test authentication and permissions for protected endpoints.

  4. Verify database changes when an API creates, updates, or deletes records.

  5. Use descriptive test names so failures are easy to understand.

  6. Keep tests independent by creating their own required test data.

  7. Document request and response formats accurately.

  8. Include authentication requirements in the API documentation.

  9. Run tests before deployment and after significant changes.

  10. Protect sensitive documentation when internal APIs should not be publicly discoverable.

Frequently Asked Questions (FAQs)

Q1. What is API testing in Django REST Framework?

API testing verifies that endpoints return the expected status codes and response data and perform the correct database operations.

Q2. What is APITestCase?

APITestCase is a Django REST Framework testing class that provides an API client and Django test-case functionality for testing HTTP endpoints.

Q3. What is the difference between APIClient and a browser?

APIClient simulates HTTP requests in automated tests. A browser is generally used to view web pages or interact with browser-based API documentation.

Q4. What is Swagger UI?

Swagger UI is an interactive interface that displays API documentation generated from an API schema and can allow users to try requests.

Q5. What is OpenAPI?

OpenAPI is a specification used to describe API endpoints, request parameters, request bodies, responses, and other API behavior in a structured format.

Q6. Do API tests require a running Django server?

Usually, no. Django's test client can test the application without manually starting a separate development server.

Q7. Why do API tests return HTTP 403?

A 403 response often indicates that the request does not have the required permission. Check the configured permission classes, user authentication, and ownership rules.

Q8. Does passing all tests guarantee an error-free API?

No. Tests only verify the scenarios they cover. Additional tests, code review, integration testing, and production monitoring remain important.

Interview Questions and Answers

1. What is the purpose of automated API testing?

Automated API testing checks endpoint behavior repeatedly and consistently, helping developers identify regressions and validate application requirements.

2. What is the difference between HTTP 200 and HTTP 201?

200 OK indicates that a request succeeded. 201 Created indicates that a request successfully created a resource.

3. What is HTTP 204 No Content?

It indicates that a request succeeded and the response contains no message body. It is commonly used for successful DELETE operations.

4. What is the difference between unit testing and integration testing?

Unit testing focuses on a smaller component or behavior. Integration testing checks how multiple components work together, such as an API view, serializer, and database.

5. Why should API validation be tested?

Validation tests confirm that incorrect or incomplete input is rejected and that clients receive predictable error responses.

6. What does force_authenticate() do?

It directly associates a user with a test request, making it useful for testing permissions without exercising the actual authentication mechanism.

7. What is an OpenAPI schema?

An OpenAPI schema is a structured description of an API's paths, operations, parameters, request bodies, responses, and security requirements.

8. What is the purpose of Swagger UI?

Swagger UI displays API documentation in an interactive interface and can allow authorized developers to try API operations.

9. Why should tests verify database changes?

A successful HTTP response alone does not prove that the intended database operation occurred. Database assertions confirm that the expected changes were actually saved or deleted.

10. Why is API documentation important in a team project?

It helps frontend developers, backend developers, testers, and external consumers understand how to use the API consistently.

Practical Assignment

Build and test a Product Management API.

Requirements:

  1. Create a Product model with name, price, and stock fields.

  2. Create a ProductSerializer.

  3. Build a ProductViewSet using ModelViewSet.

  4. Register the viewset with a DRF router.

  5. Write automated tests for GET, POST, PUT, PATCH, and DELETE.

  6. Add a test for invalid product data.

  7. Test authentication or permission rules if your API uses protected endpoints.

  8. Install and configure drf-spectacular.

  9. Add OpenAPI schema and Swagger UI routes.

  10. Run the test suite and verify the generated documentation.

Expected Result: You should have a working Product API, automated tests that verify its main behaviors, and interactive documentation that describes its available endpoints.

Conclusion

In this module, you learned how to test Django REST Framework APIs using APITestCase and APIClient, validate CRUD operations, check errors and permissions, and document API endpoints with OpenAPI and Swagger UI.

Automated testing improves reliability, while clear documentation makes APIs easier to understand and integrate with other applications.

In Module 28, you will move forward to Django REST Framework API Deployment and Production Best Practices, including environment configuration, security, logging, and preparing APIs for production use.

←
Post 26 of 29
→