Testing File Uploads in Forms

+15 Mana ✨

Introduction

File upload testing requires creating file-like objects and using the correct encoding in requests.

Key Concepts

SimpleUploadedFile: In-memory file for testing.

multipart/form-data: Required encoding for file uploads.

Deep Dive

Creating Test Files

python
from django.core.files.uploadedfile import SimpleUploadedFile

class FileUploadTests(TestCase):
    def test_form_accepts_valid_image(self):
        image = SimpleUploadedFile(
            name='test.jpg',
            content=b'\x47\x49\x46\x38\x39\x61',  # GIF header
            content_type='image/jpeg'
        )
        
        form = PhotoForm(data={}, files={'image': image})
        self.assertTrue(form.is_valid())
    
    def test_form_rejects_non_image(self):
        file = SimpleUploadedFile(
            name='test.txt',
            content=b'text content',
            content_type='text/plain'
        )
        
        form = PhotoForm(data={}, files={'image': file})
        self.assertFalse(form.is_valid())

Testing File Upload Views

python
class FileUploadViewTests(TestCase):
    def test_upload_view(self):
        self.client.force_login(self.user)
        
        image = SimpleUploadedFile(
            'test.jpg',
            b'\x89PNG\r\n\x1a\n',  # PNG header
            content_type='image/png'
        )
        
        response = self.client.post(
            reverse('upload'),
            {'title': 'Test', 'image': image},
            format='multipart'
        )
        
        self.assertEqual(response.status_code, 302)
        self.assertTrue(Photo.objects.filter(title='Test').exists())

Testing File Size Validation

python
class FileSizeValidationTests(TestCase):
    def test_rejects_large_file(self):
        # Create file larger than limit
        large_content = b'x' * (5 * 1024 * 1024 + 1)  # 5MB+
        file = SimpleUploadedFile('large.pdf', large_content)
        
        form = DocumentForm(data={}, files={'document': file})
        self.assertFalse(form.is_valid())
        self.assertIn('document', form.errors)

Real World Context

File upload testing is essential for any application that handles user-submitted images, documents, or media. Real files on disk slow tests and create cleanup headaches. SimpleUploadedFile keeps everything in memory, making tests fast and self-contained. Always test both valid and invalid file types to prevent security issues.

Common Pitfalls

  1. Using invalid file headers: Some validators check magic bytes, not just file extensions. Use real file headers in SimpleUploadedFile.
  2. Forgetting to clean up uploaded files: Test files can accumulate on disk. Use a temporary directory or override MEDIA_ROOT in test settings.
  3. Not testing file size limits: Upload size validation should be tested to prevent users from uploading excessively large files.

Best Practices

  1. Use real file headers: Some validators check magic bytes.
  2. Clean up files: Delete test files in tearDown.
  3. Use temporary storage: Configure FILE_UPLOAD_TEMP_DIR in test settings.

Summary

Use SimpleUploadedFile for creating test files. Include proper content types and file headers for realistic testing. Test both valid files and validation errors.

✓ Completed