Skip to content

Testing

Testing your API endpoints ensures reliability and prevents regressions as your application grows. DRF provides several specialized testing classes that extend Django's built-in testing framework, specifically designed to handle API-specific concepts like JSON serialization, authentication and HTTP status codes.

TIP

The primary difference between DRF's testing tools and Django's standard tools is that DRF's classes automatically handle content types, parse JSON responses and provide helpers for API authentication. This reduces boilerplate and keeps your test code DRY.

Core Test Classes

DRF provides three main classes for testing APIs, each serving a different pedagogical purpose:

  1. APITestCase: The standard way to write integration tests. It extends Django's TestCase.
  2. APIClient: The underlying mock web browser used by APITestCase to make requests.
  3. APIRequestFactory: A lower-level utility for testing views in isolation without routing through the URL dispatcher.

APITestCase

APITestCase is the most common class you'll use. It extends Django's TestCase, but replaces the standard Django Client with DRF's APIClient.

This is the recommended approach because it tests your API end-to-end, including URL routing, permissions, authentication and view logic.

Writing Your First Test

Here is an example of testing an endpoint that creates a new user.

python
from django.urls import reverse
from rest_framework import status
from rest_framework.test import APITestCase
from .models import Account

class AccountTests(APITestCase):
    def test_create_account(self):
        """
        Ensure we can create a new account object.
        """
        url = reverse('account-list')
        data = {'name': 'DabApps'}
        
        # Make the POST request
        response = self.client.post(url, data, format='json')
        
        # Verify the response
        self.assertEqual(response.status_code, status.HTTP_201_CREATED)
        self.assertEqual(Account.objects.count(), 1)
        self.assertEqual(Account.objects.get().name, 'DabApps')

Explaining the Defaults

Notice the format='json' argument in .post().

By default, if you don't specify a format, DRF's test client encodes data as multipart/form-data. While this is useful for testing file uploads, most modern REST APIs expect JSON. Passing format='json' tells the test client to encode the Python dictionary data into a JSON string and set the Content-Type: application/json header.

NOTE

You can change the default format globally in your settings.py by setting TEST_REQUEST_DEFAULT_FORMAT to 'json'.

Authentication in Tests

When testing protected endpoints, you need a way to authenticate your test requests. While you could manually pass headers or log in through an endpoint, DRF provides helpers to do this directly and efficiently.

Forcing Authentication

The .force_authenticate() method allows you to bypass the standard authentication mechanisms (like checking passwords or validating tokens) and explicitly force a request to run as a specific user.

This keeps your tests fast and focused on the view logic, rather than re-testing your authentication backend on every single endpoint.

python
from django.contrib.auth.models import User
from rest_framework.test import APIClient

class ProtectedEndpointTests(APITestCase):
    def setUp(self):
        # Create a test user
        self.user = User.objects.create_user(username='admin', password='password123')
        
    def test_restricted_data_access(self):
        # Force the client to run as our test user
        self.client.force_authenticate(user=self.user)
        
        # Now the request will be fully authenticated
        response = self.client.get('/api/restricted/')
        self.assertEqual(response.status_code, 200)

        # To clear authentication for subsequent requests:
        self.client.force_authenticate(user=None)

Passing Specific Credentials

If you want to test the actual authentication mechanism (e.g., ensuring a specific token format works), you can set the exact headers using .credentials().

python
def test_token_auth(self):
    # Set the exact HTTP header that the client would send
    self.client.credentials(HTTP_AUTHORIZATION='Token ' + self.token.key)
    
    response = self.client.get('/api/restricted/')
    self.assertEqual(response.status_code, 200)
    
    # Clear credentials
    self.client.credentials()

APIRequestFactory

While APITestCase tests the full request-response cycle, APIRequestFactory is a lower-level tool. It creates mock Request objects that you can pass directly into your view functions or classes.

Why Use APIRequestFactory?

You should use APIRequestFactory when you want to write unit tests for a view, completely bypassing Django's middleware and URL routing. This is faster but less comprehensive than APITestCase.

Example Usage

python
from rest_framework.test import APIRequestFactory
from .views import UserListView

def test_user_list_view():
    factory = APIRequestFactory()
    
    # Create the mock request
    request = factory.get('/users/', format='json')
    
    # Call the view directly
    view = UserListView.as_view()
    response = view(request)
    
    assert response.status_code == 200

WARNING

Requests generated by APIRequestFactory do not pass through Django's middleware. If your view relies on attributes set by middleware (like request.session), you must manually attach them to the request before calling the view.

Advanced Test Cases

DRF also includes specialized test cases that mirror Django's advanced testing classes:

  • APITransactionTestCase: Use this if you need to test transactions, such as committing or rolling back data within the test. Slower than APITestCase.
  • APISimpleTestCase: Use this when your tests do not require database access.
  • APILiveServerTestCase: Launches a live Django server in the background, useful for running integration tests with browser automation tools like Selenium or Playwright.