CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-djangorestframework

Web APIs for Django, made easy.

Pending
Overview
Eval results
Files

testing.mddocs/

Testing Utilities

Comprehensive testing tools including API client, request factory, and test case classes for thorough API testing in Django REST Framework.

Capabilities

API Request Factory

Factory for creating mock API requests for testing.

class APIRequestFactory(DjangoRequestFactory):
    """
    Factory for creating API requests without going through Django's WSGI interface.
    """
    def __init__(self, enforce_csrf_checks=False, **defaults):
        """
        Args:
            enforce_csrf_checks (bool): Enable CSRF checking
            **defaults: Default request parameters
        """
        self.enforce_csrf_checks = enforce_csrf_checks
        super().__init__(**defaults)
    
    def _encode_data(self, data, content_type):
        """
        Encode data for request body based on content type.
        
        Args:
            data: Data to encode
            content_type (str): Content type for encoding
            
        Returns:
            tuple: (encoded_data, content_type)
        """
    
    def get(self, path, data=None, **extra):
        """
        Create GET request.
        
        Args:
            path (str): URL path
            data (dict): Query parameters
            **extra: Additional request parameters
            
        Returns:
            Request: Mock GET request
        """
    
    def post(self, path, data=None, format=None, content_type=None, **extra):
        """
        Create POST request.
        
        Args:
            path (str): URL path
            data: Request body data
            format (str): Data format ('json', 'multipart', etc.)
            content_type (str): Custom content type
            **extra: Additional request parameters
            
        Returns:
            Request: Mock POST request
        """
    
    def put(self, path, data=None, format=None, content_type=None, **extra):
        """Create PUT request."""
    
    def patch(self, path, data=None, format=None, content_type=None, **extra):
        """Create PATCH request."""
    
    def delete(self, path, data=None, format=None, content_type=None, **extra):
        """Create DELETE request."""
    
    def options(self, path, data=None, format=None, content_type=None, **extra):
        """Create OPTIONS request."""
    
    def head(self, path, data=None, **extra):
        """Create HEAD request."""
    
    def trace(self, path, **extra):
        """Create TRACE request."""

API Test Client

Enhanced test client with API-specific functionality.

class APIClient(APIRequestFactory, DjangoClient):
    """
    Test client for making API requests in tests.
    """
    def __init__(self, enforce_csrf_checks=False, **defaults):
        """
        Args:
            enforce_csrf_checks (bool): Enable CSRF checking
            **defaults: Default client parameters
        """
        super().__init__(enforce_csrf_checks=enforce_csrf_checks, **defaults)
        self._credentials = {}
    
    def credentials(self, **kwargs):
        """
        Set authentication credentials for subsequent requests.
        
        Args:
            **kwargs: Authentication headers (HTTP_AUTHORIZATION, etc.)
        """
        self._credentials = kwargs
    
    def force_authenticate(self, user=None, token=None):
        """
        Force authentication for subsequent requests.
        
        Args:
            user: User instance to authenticate as
            token: Authentication token
        """
        self.handler._force_user = user
        self.handler._force_token = token
    
    def logout(self):
        """
        Remove authentication and credentials.
        """
        self._credentials = {}
        if hasattr(self.handler, '_force_user'):
            del self.handler._force_user
        if hasattr(self.handler, '_force_token'):
            del self.handler._force_token
        super().logout()

Test Case Classes

Specialized test case classes for API testing.

class APITestCase(testcases.TestCase):
    """
    Test case class with APIClient and additional API testing utilities.
    """
    client_class = APIClient
    
    def setUp(self):
        """Set up test case with API client."""
        super().setUp()
        self.client = self.client_class()
    
    def _pre_setup(self):
        """Pre-setup hook for test case."""
        super()._pre_setup()
    
    def _post_teardown(self):
        """Post-teardown hook for test case."""
        super()._post_teardown()

class APITransactionTestCase(testcases.TransactionTestCase):
    """
    Transaction test case for API testing with database transactions.
    """
    client_class = APIClient
    
    def _pre_setup(self):
        """Pre-setup with transaction handling."""
        super()._pre_setup()
        self.client = self.client_class()

class APISimpleTestCase(testcases.SimpleTestCase):
    """
    Simple test case for API testing without database.
    """
    client_class = APIClient
    
    def setUp(self):
        """Set up simple test case."""
        super().setUp()
        self.client = self.client_class()

class APILiveServerTestCase(testcases.LiveServerTestCase):
    """
    Live server test case for API integration testing.
    """
    client_class = APIClient
    
    def setUp(self):
        """Set up live server test case."""
        super().setUp()
        self.client = self.client_class()

class URLPatternsTestCase(testcases.SimpleTestCase):
    """
    Test case for testing URL patterns and routing.
    """
    def setUp(self):
        """Set up URL patterns test case."""
        super().setUp()
        self.client = APIClient()

Test Utilities

Utility functions for API testing.

def force_authenticate(request, user=None, token=None):
    """
    Force authentication on a request object for testing.
    
    Args:
        request: Request object to authenticate
        user: User instance to authenticate as
        token: Authentication token
    """
    request.user = user or AnonymousUser()
    request.auth = token

Usage Examples

Basic API Testing

from rest_framework.test import APITestCase, APIClient
from rest_framework import status
from django.contrib.auth.models import User
from myapp.models import Book

class BookAPITestCase(APITestCase):
    def setUp(self):
        """Set up test data."""
        self.user = User.objects.create_user(
            username='testuser',
            email='test@example.com',
            password='testpass123'
        )
        self.book = Book.objects.create(
            title='Test Book',
            author='Test Author',
            isbn='1234567890123'
        )
    
    def test_get_book_list(self):
        """Test retrieving book list."""
        url = '/api/books/'
        response = self.client.get(url)
        
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(len(response.data), 1)
        self.assertEqual(response.data[0]['title'], 'Test Book')
    
    def test_create_book_authenticated(self):
        """Test creating book with authentication."""
        self.client.force_authenticate(user=self.user)
        
        url = '/api/books/'
        data = {
            'title': 'New Book',
            'author': 'New Author',
            'isbn': '9876543210987'
        }
        response = self.client.post(url, data, format='json')
        
        self.assertEqual(response.status_code, status.HTTP_201_CREATED)
        self.assertEqual(Book.objects.count(), 2)
        self.assertEqual(response.data['title'], 'New Book')
    
    def test_create_book_unauthenticated(self):
        """Test creating book without authentication fails."""
        url = '/api/books/'
        data = {'title': 'Unauthorized Book'}
        response = self.client.post(url, data, format='json')
        
        self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)

Token Authentication Testing

from rest_framework.authtoken.models import Token

class TokenAuthTestCase(APITestCase):
    def setUp(self):
        self.user = User.objects.create_user(
            username='testuser',
            password='testpass123'
        )
        self.token = Token.objects.create(user=self.user)
    
    def test_token_authentication(self):
        """Test API access with token authentication."""
        # Set token in Authorization header
        self.client.credentials(HTTP_AUTHORIZATION='Token ' + self.token.key)
        
        url = '/api/protected-endpoint/'
        response = self.client.get(url)
        
        self.assertEqual(response.status_code, status.HTTP_200_OK)
    
    def test_invalid_token(self):
        """Test API access with invalid token."""
        self.client.credentials(HTTP_AUTHORIZATION='Token invalid-token')
        
        url = '/api/protected-endpoint/'
        response = self.client.get(url)
        
        self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)
    
    def test_no_token(self):
        """Test API access without token."""
        url = '/api/protected-endpoint/'
        response = self.client.get(url)
        
        self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)

Testing Different Content Types

class ContentTypeTestCase(APITestCase):
    def test_json_request(self):
        """Test JSON content type."""
        url = '/api/books/'
        data = {'title': 'JSON Book', 'author': 'JSON Author'}
        response = self.client.post(url, data, format='json')
        
        self.assertEqual(response.status_code, status.HTTP_201_CREATED)
    
    def test_form_request(self):
        """Test form-encoded content type."""
        url = '/api/books/'
        data = {'title': 'Form Book', 'author': 'Form Author'}
        response = self.client.post(url, data)  # Default is form-encoded
        
        self.assertEqual(response.status_code, status.HTTP_201_CREATED)
    
    def test_multipart_request(self):
        """Test multipart content type with file upload."""
        url = '/api/books/'
        with open('test_cover.jpg', 'rb') as cover_file:
            data = {
                'title': 'Book with Cover',
                'author': 'Cover Author',
                'cover_image': cover_file
            }
            response = self.client.post(url, data, format='multipart')
        
        self.assertEqual(response.status_code, status.HTTP_201_CREATED)

Testing Custom Headers and Parameters

class CustomHeaderTestCase(APITestCase):
    def test_custom_header(self):
        """Test API with custom headers."""
        url = '/api/books/'
        response = self.client.get(
            url,
            HTTP_X_CUSTOM_HEADER='custom-value',
            HTTP_ACCEPT='application/json'
        )
        
        self.assertEqual(response.status_code, status.HTTP_200_OK)
    
    def test_query_parameters(self):
        """Test API with query parameters."""
        url = '/api/books/'
        response = self.client.get(url, {
            'search': 'django',
            'ordering': 'title',
            'page': 1
        })
        
        self.assertEqual(response.status_code, status.HTTP_200_OK)

Testing Pagination

class PaginationTestCase(APITestCase):
    def setUp(self):
        # Create multiple books for pagination testing
        for i in range(25):
            Book.objects.create(
                title=f'Book {i}',
                author=f'Author {i}',
                isbn=f'123456789012{i}'
            )
    
    def test_pagination_first_page(self):
        """Test first page of paginated results."""
        url = '/api/books/'
        response = self.client.get(url)
        
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertIn('results', response.data)
        self.assertIn('next', response.data)
        self.assertIsNone(response.data['previous'])
        self.assertEqual(len(response.data['results']), 20)  # Default page size
    
    def test_pagination_custom_page_size(self):
        """Test custom page size."""
        url = '/api/books/?page_size=10'
        response = self.client.get(url)
        
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(len(response.data['results']), 10)

Testing Error Handling

class ErrorHandlingTestCase(APITestCase):
    def test_validation_error(self):
        """Test validation error responses."""
        url = '/api/books/'
        data = {'title': ''}  # Invalid: empty title
        response = self.client.post(url, data, format='json')
        
        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
        self.assertIn('title', response.data)
    
    def test_not_found_error(self):
        """Test 404 error for non-existent resource."""
        url = '/api/books/999/'  # Non-existent book ID
        response = self.client.get(url)
        
        self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
    
    def test_method_not_allowed(self):
        """Test 405 error for unsupported method."""
        url = '/api/books/1/'
        response = self.client.patch(url, {'title': 'Updated'}, format='json')
        
        # Assuming PATCH is not allowed on this endpoint
        self.assertEqual(response.status_code, status.HTTP_405_METHOD_NOT_ALLOWED)

Unit Testing Views Directly

from rest_framework.test import APIRequestFactory
from myapp.views import BookViewSet

class ViewUnitTestCase(APITestCase):
    def setUp(self):
        self.factory = APIRequestFactory()
        self.user = User.objects.create_user(
            username='testuser',
            password='testpass123'
        )
    
    def test_viewset_list_action(self):
        """Test viewset list action directly."""
        # Create request
        request = self.factory.get('/api/books/')
        request.user = self.user
        
        # Test view directly
        view = BookViewSet.as_view({'get': 'list'})
        response = view(request)
        
        self.assertEqual(response.status_code, status.HTTP_200_OK)
    
    def test_viewset_create_action(self):
        """Test viewset create action directly."""
        data = {'title': 'Direct Test Book', 'author': 'Test Author'}
        request = self.factory.post('/api/books/', data, format='json')
        request.user = self.user
        
        view = BookViewSet.as_view({'post': 'create'})
        response = view(request)
        
        self.assertEqual(response.status_code, status.HTTP_201_CREATED)

Mocking External Services

from unittest.mock import patch, Mock

class ExternalServiceTestCase(APITestCase):
    @patch('myapp.services.external_api_call')
    def test_with_mocked_service(self, mock_api_call):
        """Test API endpoint that calls external service."""
        # Configure mock
        mock_api_call.return_value = {'status': 'success', 'data': 'mocked'}
        
        url = '/api/books/sync/'
        response = self.client.post(url, format='json')
        
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        mock_api_call.assert_called_once()

Install with Tessl CLI

npx tessl i tessl/pypi-djangorestframework

docs

auth-permissions.md

content-negotiation.md

decorators.md

fields-validation.md

generic-views.md

index.md

pagination-filtering.md

request-response.md

routers-urls.md

serializers.md

status-exceptions.md

testing.md

views-viewsets.md

tile.json