Subjects

All subjects Django Java Python React Spring Boot JavaScript PHP
Sign Up Free
Question 8 of 10 · Django Signals & Decorators
Interview question

How do you test Django signals to ensure they work correctly? Django signals को test कैसे करते हैं - ensure करने के लिए वे correctly काम करें?

Answer

Test signals by disconnecting them to isolate behavior, using mock objects to verify calls, and checking that expected side effects occur. Signals can be tricky to test without proper isolation.

// Testing Signal Handlers
from django.test import TestCase
from django.db.models.signals import post_save, post_delete
from unittest.mock import patch, MagicMock
from .models import Book
from .signals import send_email_on_book_created

class SignalTestCase(TestCase):
    def setUp(self):
        # Disconnect signals for cleaner tests
        post_save.disconnect(send_email_on_book_created, sender=Book)
    
    def tearDown(self):
        # Reconnect signals after test
        post_save.connect(send_email_on_book_created, sender=Book)
    
    // Test signal is called
    @patch("myapp.signals.send_notification_email")
    def test_email_sent_on_book_creation(self, mock_email):
        book = Book.objects.create(
            title="Test Book",
            author="Test Author",
            price=9.99
        )
        
        # Manually trigger signal for isolated test
        send_email_on_book_created(sender=Book, instance=book, created=True)
        
        # Verify email was called
        mock_email.assert_called_once_with(book)
    
    // Test signal with mock
    @patch("myapp.signals.update_cache")
    def test_cache_updated(self, mock_cache):
        book = Book.objects.create(title="Test", author="Author", price=10)
        
        mock_cache.assert_called_once()
        call_args = mock_cache.call_args
        self.assertEqual(call_args[0][1].title, "Test")

// Test with side effects
class SignalSideEffectTest(TestCase):
    def test_book_update_creates_audit_log(self):
        book = Book.objects.create(title="Original", author="Author", price=10)
        
        # Update book
        book.title = "Updated"
        book.save()
        
        # Check if audit log was created
        from .models import AuditLog
        audit = AuditLog.objects.get(object_id=book.id)
        self.assertEqual(audit.action, "updated")
        self.assertEqual(audit.old_value, "Original")
        self.assertEqual(audit.new_value, "Updated")

// Test signal not called
class SignalNotCalledTest(TestCase):
    @patch("myapp.signals.expensive_operation")
    def test_expensive_operation_not_called_on_update(self, mock_op):
        book = Book.objects.create(title="Test", author="Author", price=10)
        mock_op.reset_mock()
        
        # Update shouldn't trigger expensive operation
        book.price = 15
        book.save()
        
        mock_op.assert_not_called()

// Integration Test - with real side effects
class SignalIntegrationTest(TestCase):
    def test_creating_book_updates_category_count(self):
        category = Category.objects.create(name="Fiction")
        initial_count = category.book_count
        
        book = Book.objects.create(
            title="Test",
            author="Author",
            price=10,
            category=category
        )
        
        category.refresh_from_db()
        self.assertEqual(category.book_count, initial_count + 1)

// Disabling signals in bulk operations
class BulkOperationTest(TestCase):
    @patch("django.db.models.signals.post_save.send")
    def test_bulk_create_efficiency(self, mock_signal):
        # Bulk operations bypass signals for efficiency
        books = [
            Book(title=f"Book {i}", author="Author", price=10)
            for i in range(100)
        ]
        
        created = Book.objects.bulk_create(books)
        
        # Signals are NOT sent for bulk_create
        self.assertEqual(len(created), 100)
        mock_signal.assert_not_called()

Signals को test करने के लिए उन्हें disconnect करो, mock objects use करो, और side effects check करो।

from django.test import TestCase
from django.db.models.signals import post_save
from unittest.mock import patch

class SignalTest(TestCase):
    def setUp(self):
        post_save.disconnect(signal_handler, sender=Book)
    
    def tearDown(self):
        post_save.connect(signal_handler, sender=Book)
    
    @patch("myapp.signals.send_email")
    def test_email_sent(self, mock_email):
        book = Book.objects.create(title="Test", author="Author")
        signal_handler(sender=Book, instance=book, created=True)
        mock_email.assert_called_once()

Was this answer clear?