Testing

Testing Views

View testing is crucial for ensuring your Django application handles HTTP requests correctly, renders appropriate responses, and enforces proper access controls. Views are the interface between your users and your application logic, making comprehensive view testing essential for a reliable web application.

Testing Views

View testing is crucial for ensuring your Django application handles HTTP requests correctly, renders appropriate responses, and enforces proper access controls. Views are the interface between your users and your application logic, making comprehensive view testing essential for a reliable web application.

Basic View Testing

Testing Function-Based Views

from django.test import TestCase, Client
from django.urls import reverse
from django.contrib.auth.models import User
from django.http import Http404
from blog.models import BlogPost, Category

class BlogViewTests(TestCase):
    """Test blog views"""
    
    def setUp(self):
        """Set up test data"""
        self.client = Client()
        self.user = User.objects.create_user(
            username='testuser',
            email='test@example.com',
            password='testpass123'
        )
        
        self.category = Category.objects.create(
            name='Technology',
            slug='technology'
        )
        
        self.post = BlogPost.objects.create(
            title='Test Post',
            slug='test-post',
            content='This is test content.',
            author=self.user,
            category=self.category,
            status='published'
        )
    
    def test_post_list_view_get(self):
        """Test blog post list view GET request"""
        
        url = reverse('blog:post_list')
        response = self.client.get(url)
        
        # Check response status
        self.assertEqual(response.status_code, 200)
        
        # Check template used
        self.assertTemplateUsed(response, 'blog/post_list.html')
        
        # Check context data
        self.assertIn('posts', response.context)
        self.assertIn(self.post, response.context['posts'])
        
        # Check response content
        self.assertContains(response, self.post.title)
        self.assertContains(response, self.post.author.username)
    
    def test_post_detail_view_get(self):
        """Test blog post detail view GET request"""
        
        url = reverse('blog:post_detail', kwargs={'slug': self.post.slug})
        response = self.client.get(url)
        
        # Check response status
        self.assertEqual(response.status_code, 200)
        
        # Check template used
        self.assertTemplateUsed(response, 'blog/post_detail.html')
        
        # Check context data
        self.assertEqual(response.context['post'], self.post)
        
        # Check response content
        self.assertContains(response, self.post.title)
        self.assertContains(response, self.post.content)
        self.assertContains(response, self.post.author.username)
    
    def test_post_detail_view_nonexistent_post(self):
        """Test post detail view with nonexistent post"""
        
        url = reverse('blog:post_detail', kwargs={'slug': 'nonexistent-post'})
        response = self.client.get(url)
        
        # Should return 404
        self.assertEqual(response.status_code, 404)
    
    def test_post_create_view_get_authenticated(self):
        """Test post create view GET request when authenticated"""
        
        self.client.login(username='testuser', password='testpass123')
        
        url = reverse('blog:post_create')
        response = self.client.get(url)
        
        self.assertEqual(response.status_code, 200)
        self.assertTemplateUsed(response, 'blog/post_form.html')
        self.assertIn('form', response.context)
    
    def test_post_create_view_get_anonymous(self):
        """Test post create view GET request when anonymous"""
        
        url = reverse('blog:post_create')
        response = self.client.get(url)
        
        # Should redirect to login
        self.assertEqual(response.status_code, 302)
        self.assertRedirects(response, f'/login/?next={url}')
    
    def test_post_create_view_post_valid_data(self):
        """Test post create view POST request with valid data"""
        
        self.client.login(username='testuser', password='testpass123')
        
        url = reverse('blog:post_create')
        data = {
            'title': 'New Test Post',
            'content': 'This is new test content.',
            'category': self.category.id,
            'status': 'published'
        }
        
        response = self.client.post(url, data)
        
        # Should redirect after successful creation
        self.assertEqual(response.status_code, 302)
        
        # Check post was created
        new_post = BlogPost.objects.get(title='New Test Post')
        self.assertEqual(new_post.author, self.user)
        self.assertEqual(new_post.category, self.category)
        
        # Check redirect location
        self.assertRedirects(response, new_post.get_absolute_url())
    
    def test_post_create_view_post_invalid_data(self):
        """Test post create view POST request with invalid data"""
        
        self.client.login(username='testuser', password='testpass123')
        
        url = reverse('blog:post_create')
        data = {
            'title': '',  # Invalid: empty title
            'content': 'Content',
            'category': self.category.id
        }
        
        response = self.client.post(url, data)
        
        # Should return form with errors
        self.assertEqual(response.status_code, 200)
        self.assertTemplateUsed(response, 'blog/post_form.html')
        self.assertFormError(response, 'form', 'title', 'This field is required.')
        
        # Check no post was created
        self.assertFalse(BlogPost.objects.filter(title='').exists())

Testing Class-Based Views

from django.test import TestCase
from django.urls import reverse
from django.contrib.auth.models import User
from django.core.paginator import Page

class BlogClassBasedViewTests(TestCase):
    """Test class-based views"""
    
    def setUp(self):
        self.user = User.objects.create_user(
            username='testuser',
            password='testpass123'
        )
        
        self.category = Category.objects.create(
            name='Technology',
            slug='technology'
        )
        
        # Create multiple posts for pagination testing
        for i in range(25):
            BlogPost.objects.create(
                title=f'Test Post {i}',
                slug=f'test-post-{i}',
                content=f'Content for post {i}',
                author=self.user,
                category=self.category,
                status='published'
            )
    
    def test_post_list_view_pagination(self):
        """Test ListView pagination"""
        
        url = reverse('blog:post_list')
        response = self.client.get(url)
        
        self.assertEqual(response.status_code, 200)
        
        # Check pagination context
        self.assertIn('is_paginated', response.context)
        self.assertTrue(response.context['is_paginated'])
        
        # Check page object
        page_obj = response.context['page_obj']
        self.assertIsInstance(page_obj, Page)
        self.assertEqual(page_obj.number, 1)
        
        # Check posts per page (assuming 20 per page)
        posts = response.context['posts']
        self.assertEqual(len(posts), 20)
    
    def test_post_list_view_second_page(self):
        """Test ListView second page"""
        
        url = reverse('blog:post_list')
        response = self.client.get(url, {'page': 2})
        
        self.assertEqual(response.status_code, 200)
        
        page_obj = response.context['page_obj']
        self.assertEqual(page_obj.number, 2)
        
        # Should have remaining posts
        posts = response.context['posts']
        self.assertEqual(len(posts), 5)  # 25 total - 20 on first page
    
    def test_post_list_view_invalid_page(self):
        """Test ListView with invalid page number"""
        
        url = reverse('blog:post_list')
        response = self.client.get(url, {'page': 999})
        
        # Should show last page
        self.assertEqual(response.status_code, 200)
        
        page_obj = response.context['page_obj']
        self.assertEqual(page_obj.number, 2)  # Last page
    
    def test_post_detail_view_context(self):
        """Test DetailView context"""
        
        post = BlogPost.objects.first()
        url = reverse('blog:post_detail', kwargs={'slug': post.slug})
        response = self.client.get(url)
        
        self.assertEqual(response.status_code, 200)
        self.assertEqual(response.context['post'], post)
        self.assertEqual(response.context['object'], post)  # Generic context name
    
    def test_post_update_view_owner_access(self):
        """Test UpdateView access by post owner"""
        
        post = BlogPost.objects.first()
        self.client.login(username='testuser', password='testpass123')
        
        url = reverse('blog:post_update', kwargs={'slug': post.slug})
        response = self.client.get(url)
        
        self.assertEqual(response.status_code, 200)
        self.assertTemplateUsed(response, 'blog/post_form.html')
        
        # Check form is pre-populated
        form = response.context['form']
        self.assertEqual(form.initial['title'], post.title)
    
    def test_post_update_view_non_owner_access(self):
        """Test UpdateView access by non-owner"""
        
        # Create another user
        other_user = User.objects.create_user(
            username='otheruser',
            password='otherpass123'
        )
        
        post = BlogPost.objects.first()
        self.client.login(username='otheruser', password='otherpass123')
        
        url = reverse('blog:post_update', kwargs={'slug': post.slug})
        response = self.client.get(url)
        
        # Should be forbidden or redirect
        self.assertIn(response.status_code, [403, 302])
    
    def test_post_delete_view_confirmation(self):
        """Test DeleteView confirmation page"""
        
        post = BlogPost.objects.first()
        self.client.login(username='testuser', password='testpass123')
        
        url = reverse('blog:post_delete', kwargs={'slug': post.slug})
        response = self.client.get(url)
        
        self.assertEqual(response.status_code, 200)
        self.assertTemplateUsed(response, 'blog/post_confirm_delete.html')
        self.assertEqual(response.context['post'], post)
    
    def test_post_delete_view_post_request(self):
        """Test DeleteView POST request"""
        
        post = BlogPost.objects.first()
        post_id = post.id
        
        self.client.login(username='testuser', password='testpass123')
        
        url = reverse('blog:post_delete', kwargs={'slug': post.slug})
        response = self.client.post(url)
        
        # Should redirect after deletion
        self.assertEqual(response.status_code, 302)
        
        # Check post was deleted
        self.assertFalse(BlogPost.objects.filter(id=post_id).exists())
        
        # Check redirect location
        self.assertRedirects(response, reverse('blog:post_list'))

Testing Authentication and Permissions

Testing Login Required Views

from django.contrib.auth.decorators import login_required
from django.contrib.auth.mixins import LoginRequiredMixin

class AuthenticationTests(TestCase):
    """Test authentication requirements"""
    
    def setUp(self):
        self.user = User.objects.create_user(
            username='testuser',
            password='testpass123'
        )
        
        self.category = Category.objects.create(
            name='Technology',
            slug='technology'
        )
    
    def test_login_required_view_anonymous_user(self):
        """Test login required view with anonymous user"""
        
        url = reverse('blog:post_create')
        response = self.client.get(url)
        
        # Should redirect to login
        self.assertEqual(response.status_code, 302)
        
        # Check redirect URL includes next parameter
        expected_url = f'/accounts/login/?next={url}'
        self.assertRedirects(response, expected_url)
    
    def test_login_required_view_authenticated_user(self):
        """Test login required view with authenticated user"""
        
        self.client.login(username='testuser', password='testpass123')
        
        url = reverse('blog:post_create')
        response = self.client.get(url)
        
        # Should allow access
        self.assertEqual(response.status_code, 200)
    
    def test_force_login_method(self):
        """Test using force_login for authentication"""
        
        # force_login bypasses authentication backend
        self.client.force_login(self.user)
        
        url = reverse('blog:post_create')
        response = self.client.get(url)
        
        self.assertEqual(response.status_code, 200)
    
    def test_logout_functionality(self):
        """Test user logout"""
        
        # Login first
        self.client.login(username='testuser', password='testpass123')
        
        # Access protected view (should work)
        url = reverse('blog:post_create')
        response = self.client.get(url)
        self.assertEqual(response.status_code, 200)
        
        # Logout
        self.client.logout()
        
        # Try to access protected view again (should redirect)
        response = self.client.get(url)
        self.assertEqual(response.status_code, 302)

Testing Permission-Based Views

from django.contrib.auth.models import Permission
from django.contrib.contenttypes.models import ContentType

class PermissionTests(TestCase):
    """Test permission-based access control"""
    
    def setUp(self):
        # Create users
        self.regular_user = User.objects.create_user(
            username='regular',
            password='pass123'
        )
        
        self.editor_user = User.objects.create_user(
            username='editor',
            password='pass123'
        )
        
        self.admin_user = User.objects.create_superuser(
            username='admin',
            password='pass123'
        )
        
        # Add editor permission to editor_user
        content_type = ContentType.objects.get_for_model(BlogPost)
        edit_permission = Permission.objects.get(
            codename='change_blogpost',
            content_type=content_type
        )
        self.editor_user.user_permissions.add(edit_permission)
        
        self.category = Category.objects.create(name='Tech', slug='tech')
        self.post = BlogPost.objects.create(
            title='Test Post',
            content='Content',
            author=self.regular_user,
            category=self.category
        )
    
    def test_admin_required_view_regular_user(self):
        """Test admin required view with regular user"""
        
        self.client.login(username='regular', password='pass123')
        
        url = reverse('admin:blog_blogpost_changelist')
        response = self.client.get(url)
        
        # Should redirect to admin login
        self.assertEqual(response.status_code, 302)
    
    def test_admin_required_view_admin_user(self):
        """Test admin required view with admin user"""
        
        self.client.login(username='admin', password='pass123')
        
        url = reverse('admin:blog_blogpost_changelist')
        response = self.client.get(url)
        
        # Should allow access
        self.assertEqual(response.status_code, 200)
    
    def test_permission_required_view_without_permission(self):
        """Test permission required view without permission"""
        
        self.client.login(username='regular', password='pass123')
        
        # Assuming a view that requires 'change_blogpost' permission
        url = reverse('blog:post_edit_admin', kwargs={'pk': self.post.pk})
        response = self.client.get(url)
        
        # Should be forbidden
        self.assertEqual(response.status_code, 403)
    
    def test_permission_required_view_with_permission(self):
        """Test permission required view with permission"""
        
        self.client.login(username='editor', password='pass123')
        
        url = reverse('blog:post_edit_admin', kwargs={'pk': self.post.pk})
        response = self.client.get(url)
        
        # Should allow access
        self.assertEqual(response.status_code, 200)
    
    def test_superuser_access(self):
        """Test superuser access to all views"""
        
        self.client.login(username='admin', password='pass123')
        
        # Superuser should have access to all views
        urls = [
            reverse('blog:post_edit_admin', kwargs={'pk': self.post.pk}),
            reverse('admin:blog_blogpost_changelist'),
        ]
        
        for url in urls:
            response = self.client.get(url)
            self.assertIn(response.status_code, [200, 302])  # 302 for redirects

Testing AJAX and API Views

Testing AJAX Views

import json
from django.http import JsonResponse

class AjaxViewTests(TestCase):
    """Test AJAX views"""
    
    def setUp(self):
        self.user = User.objects.create_user(
            username='testuser',
            password='testpass123'
        )
        
        self.category = Category.objects.create(name='Tech', slug='tech')
        self.post = BlogPost.objects.create(
            title='Test Post',
            content='Content',
            author=self.user,
            category=self.category
        )
    
    def test_ajax_like_post_authenticated(self):
        """Test AJAX like post view when authenticated"""
        
        self.client.login(username='testuser', password='testpass123')
        
        url = reverse('blog:ajax_like_post')
        response = self.client.post(
            url,
            {'post_id': self.post.id},
            HTTP_X_REQUESTED_WITH='XMLHttpRequest'
        )
        
        # Check response
        self.assertEqual(response.status_code, 200)
        self.assertEqual(response['Content-Type'], 'application/json')
        
        # Parse JSON response
        data = json.loads(response.content)
        self.assertTrue(data['success'])
        self.assertIn('likes_count', data)
    
    def test_ajax_like_post_anonymous(self):
        """Test AJAX like post view when anonymous"""
        
        url = reverse('blog:ajax_like_post')
        response = self.client.post(
            url,
            {'post_id': self.post.id},
            HTTP_X_REQUESTED_WITH='XMLHttpRequest'
        )
        
        # Should return error
        self.assertEqual(response.status_code, 401)
        
        data = json.loads(response.content)
        self.assertFalse(data['success'])
        self.assertIn('error', data)
    
    def test_ajax_search_posts(self):
        """Test AJAX search posts view"""
        
        # Create additional posts
        BlogPost.objects.create(
            title='Django Tutorial',
            content='Learn Django',
            author=self.user,
            category=self.category
        )
        
        BlogPost.objects.create(
            title='Python Guide',
            content='Learn Python',
            author=self.user,
            category=self.category
        )
        
        url = reverse('blog:ajax_search_posts')
        response = self.client.get(
            url,
            {'q': 'Django'},
            HTTP_X_REQUESTED_WITH='XMLHttpRequest'
        )
        
        self.assertEqual(response.status_code, 200)
        
        data = json.loads(response.content)
        self.assertIn('results', data)
        self.assertEqual(len(data['results']), 1)
        self.assertEqual(data['results'][0]['title'], 'Django Tutorial')
    
    def test_non_ajax_request_to_ajax_view(self):
        """Test non-AJAX request to AJAX-only view"""
        
        url = reverse('blog:ajax_like_post')
        response = self.client.post(url, {'post_id': self.post.id})
        
        # Should return bad request or redirect
        self.assertIn(response.status_code, [400, 405])

Testing API Views

from rest_framework.test import APITestCase
from rest_framework import status
from django.urls import reverse

class BlogAPITests(APITestCase):
    """Test REST API views"""
    
    def setUp(self):
        self.user = User.objects.create_user(
            username='testuser',
            password='testpass123'
        )
        
        self.category = Category.objects.create(name='Tech', slug='tech')
        self.post = BlogPost.objects.create(
            title='Test Post',
            content='Content',
            author=self.user,
            category=self.category,
            status='published'
        )
    
    def test_api_post_list_get(self):
        """Test API post list GET request"""
        
        url = reverse('api:post-list')
        response = self.client.get(url)
        
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(len(response.data['results']), 1)
        self.assertEqual(response.data['results'][0]['title'], 'Test Post')
    
    def test_api_post_detail_get(self):
        """Test API post detail GET request"""
        
        url = reverse('api:post-detail', kwargs={'pk': self.post.pk})
        response = self.client.get(url)
        
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(response.data['title'], 'Test Post')
        self.assertEqual(response.data['author'], self.user.username)
    
    def test_api_post_create_authenticated(self):
        """Test API post creation when authenticated"""
        
        self.client.force_authenticate(user=self.user)
        
        url = reverse('api:post-list')
        data = {
            'title': 'New API Post',
            'content': 'Content created via API',
            'category': self.category.id,
            'status': 'published'
        }
        
        response = self.client.post(url, data)
        
        self.assertEqual(response.status_code, status.HTTP_201_CREATED)
        self.assertEqual(response.data['title'], 'New API Post')
        
        # Check post was created in database
        new_post = BlogPost.objects.get(title='New API Post')
        self.assertEqual(new_post.author, self.user)
    
    def test_api_post_create_unauthenticated(self):
        """Test API post creation when unauthenticated"""
        
        url = reverse('api:post-list')
        data = {
            'title': 'Unauthorized Post',
            'content': 'This should fail',
            'category': self.category.id
        }
        
        response = self.client.post(url, data)
        
        self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)
    
    def test_api_post_update_owner(self):
        """Test API post update by owner"""
        
        self.client.force_authenticate(user=self.user)
        
        url = reverse('api:post-detail', kwargs={'pk': self.post.pk})
        data = {
            'title': 'Updated Title',
            'content': self.post.content,
            'category': self.category.id,
            'status': self.post.status
        }
        
        response = self.client.put(url, data)
        
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(response.data['title'], 'Updated Title')
        
        # Check database was updated
        self.post.refresh_from_db()
        self.assertEqual(self.post.title, 'Updated Title')
    
    def test_api_post_delete_owner(self):
        """Test API post deletion by owner"""
        
        self.client.force_authenticate(user=self.user)
        
        url = reverse('api:post-detail', kwargs={'pk': self.post.pk})
        response = self.client.delete(url)
        
        self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
        
        # Check post was deleted
        self.assertFalse(BlogPost.objects.filter(pk=self.post.pk).exists())

Testing Form Handling in Views

Testing Form Validation in Views

class FormHandlingTests(TestCase):
    """Test form handling in views"""
    
    def setUp(self):
        self.user = User.objects.create_user(
            username='testuser',
            password='testpass123'
        )
        
        self.category = Category.objects.create(name='Tech', slug='tech')
        self.client.login(username='testuser', password='testpass123')
    
    def test_contact_form_valid_submission(self):
        """Test contact form with valid data"""
        
        url = reverse('blog:contact')
        data = {
            'name': 'John Doe',
            'email': 'john@example.com',
            'subject': 'Test Subject',
            'message': 'This is a test message.'
        }
        
        response = self.client.post(url, data)
        
        # Should redirect after successful submission
        self.assertEqual(response.status_code, 302)
        self.assertRedirects(response, reverse('blog:contact_success'))
    
    def test_contact_form_invalid_email(self):
        """Test contact form with invalid email"""
        
        url = reverse('blog:contact')
        data = {
            'name': 'John Doe',
            'email': 'invalid-email',  # Invalid email format
            'subject': 'Test Subject',
            'message': 'This is a test message.'
        }
        
        response = self.client.post(url, data)
        
        # Should return form with errors
        self.assertEqual(response.status_code, 200)
        self.assertFormError(
            response, 
            'form', 
            'email', 
            'Enter a valid email address.'
        )
    
    def test_contact_form_missing_required_fields(self):
        """Test contact form with missing required fields"""
        
        url = reverse('blog:contact')
        data = {
            'name': '',  # Missing required field
            'email': 'john@example.com',
            'subject': '',  # Missing required field
            'message': 'This is a test message.'
        }
        
        response = self.client.post(url, data)
        
        self.assertEqual(response.status_code, 200)
        
        # Check multiple form errors
        self.assertFormError(response, 'form', 'name', 'This field is required.')
        self.assertFormError(response, 'form', 'subject', 'This field is required.')
    
    def test_post_form_with_file_upload(self):
        """Test post form with file upload"""
        
        from django.core.files.uploadedfile import SimpleUploadedFile
        
        # Create test file
        test_file = SimpleUploadedFile(
            "test_image.jpg",
            b"fake image content",
            content_type="image/jpeg"
        )
        
        url = reverse('blog:post_create')
        data = {
            'title': 'Post with Image',
            'content': 'This post has an image.',
            'category': self.category.id,
            'status': 'published',
            'featured_image': test_file
        }
        
        response = self.client.post(url, data)
        
        self.assertEqual(response.status_code, 302)
        
        # Check post was created with image
        post = BlogPost.objects.get(title='Post with Image')
        self.assertTrue(post.featured_image)
    
    def test_form_initial_data(self):
        """Test form with initial data"""
        
        post = BlogPost.objects.create(
            title='Existing Post',
            content='Existing content',
            author=self.user,
            category=self.category
        )
        
        url = reverse('blog:post_update', kwargs={'slug': post.slug})
        response = self.client.get(url)
        
        self.assertEqual(response.status_code, 200)
        
        # Check form is pre-populated
        form = response.context['form']
        self.assertEqual(form.initial['title'], 'Existing Post')
        self.assertEqual(form.initial['content'], 'Existing content')

Testing Custom View Mixins

Testing Reusable View Mixins

# views.py
from django.contrib.auth.mixins import LoginRequiredMixin
from django.core.exceptions import PermissionDenied

class OwnerRequiredMixin:
    """Mixin to ensure only object owner can access view"""
    
    def dispatch(self, request, *args, **kwargs):
        obj = self.get_object()
        if obj.author != request.user:
            raise PermissionDenied("You don't have permission to access this.")
        return super().dispatch(request, *args, **kwargs)

class AjaxRequiredMixin:
    """Mixin to ensure view is only accessed via AJAX"""
    
    def dispatch(self, request, *args, **kwargs):
        if not request.is_ajax():
            return JsonResponse({'error': 'AJAX required'}, status=400)
        return super().dispatch(request, *args, **kwargs)

# tests.py
class ViewMixinTests(TestCase):
    """Test custom view mixins"""
    
    def setUp(self):
        self.user1 = User.objects.create_user('user1', 'user1@example.com', 'pass')
        self.user2 = User.objects.create_user('user2', 'user2@example.com', 'pass')
        
        self.category = Category.objects.create(name='Tech', slug='tech')
        self.post = BlogPost.objects.create(
            title='Test Post',
            content='Content',
            author=self.user1,
            category=self.category
        )
    
    def test_owner_required_mixin_owner_access(self):
        """Test OwnerRequiredMixin allows owner access"""
        
        self.client.login(username='user1', password='pass')
        
        url = reverse('blog:post_update', kwargs={'slug': self.post.slug})
        response = self.client.get(url)
        
        self.assertEqual(response.status_code, 200)
    
    def test_owner_required_mixin_non_owner_access(self):
        """Test OwnerRequiredMixin denies non-owner access"""
        
        self.client.login(username='user2', password='pass')
        
        url = reverse('blog:post_update', kwargs={'slug': self.post.slug})
        response = self.client.get(url)
        
        self.assertEqual(response.status_code, 403)
    
    def test_ajax_required_mixin_ajax_request(self):
        """Test AjaxRequiredMixin allows AJAX requests"""
        
        url = reverse('blog:ajax_view')
        response = self.client.get(
            url,
            HTTP_X_REQUESTED_WITH='XMLHttpRequest'
        )
        
        self.assertEqual(response.status_code, 200)
    
    def test_ajax_required_mixin_non_ajax_request(self):
        """Test AjaxRequiredMixin rejects non-AJAX requests"""
        
        url = reverse('blog:ajax_view')
        response = self.client.get(url)
        
        self.assertEqual(response.status_code, 400)
        
        data = json.loads(response.content)
        self.assertIn('error', data)

Testing Error Handling

Testing Error Views

class ErrorHandlingTests(TestCase):
    """Test error handling in views"""
    
    def test_404_error_handling(self):
        """Test 404 error handling"""
        
        url = '/nonexistent-page/'
        response = self.client.get(url)
        
        self.assertEqual(response.status_code, 404)
    
    def test_500_error_handling(self):
        """Test 500 error handling"""
        
        # This would test a view that intentionally raises an exception
        # In practice, you'd mock a service to raise an exception
        
        with self.assertRaises(Exception):
            # Code that raises an exception
            raise Exception("Test exception")
    
    def test_custom_error_page_content(self):
        """Test custom error page content"""
        
        # Assuming you have custom 404 template
        url = '/nonexistent-page/'
        response = self.client.get(url)
        
        self.assertEqual(response.status_code, 404)
        self.assertContains(response, 'Page Not Found', status_code=404)
        self.assertTemplateUsed(response, '404.html')

Next Steps

With comprehensive view testing in place, you're ready to move on to testing forms. The next chapter will cover testing Django forms, including field validation, custom validation methods, form rendering, and form processing logic.

Key view testing concepts covered:

  • Basic GET and POST request testing
  • Authentication and permission testing
  • AJAX and API view testing
  • Form handling and validation testing
  • Custom mixin testing
  • Error handling testing

View tests ensure your application's user interface works correctly and securely, providing confidence in your application's behavior from the user's perspective.