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?