When django tests are running, database transactions are not committed. How do I test an event triggered by object creation but that happens after the db transaction has been committed?
I have a Campaign
model and the below post_save
signal. Using Django TestCase it is difficult to assert that the functions within transaction.on_commit
are called in the case when a new Campaign object is created. When the signal runs in the test context, it always thinks that an existing campaign object is being edited, not that one has been newly created. Therefore I cannot test the else
branch of the if statement.
How could I test the case when Campaign.objects.filter(pk=instance.pk).exists()
is False?
Signal:
@receiver(post_save, sender=Campaign, dispatch_uid="apps.writing.signals.create_handwriting")
def create_handwriting(sender, instance, **kwargs):
"""Whenever a campaign is created or updated, trigger the handwriting cloud function to (re)generate the
handwriting image.
"""
if Campaign.objects.filter(pk=instance.pk).exists():
transaction.on_commit(
lambda: log_campaign_progress(pk=instance.pk, status="t2h-edited", stage="campaign")
)
transaction.on_commit(lambda: delete_campaign_pages(campaign_pk=instance.pk))
else:
transaction.on_commit(
lambda: log_campaign_progress(pk=instance.pk, status="t2h-created", stage="campaign")
)
transaction.on_commit(lambda: enqueue_handwriting_generation(campaign_pk=instance.pk))
Test:
class TestSignals(TestCase):
def setUp(self):
self.factory = RequestFactory()
@mock.patch("lettergun.apps.writing.signals.log_campaign_progress")
@mock.patch("lettergun.apps.writing.signals.enqueue_handwriting_generation")
@mock.patch("lettergun.apps.writing.signals.delete_campaign_pages")
def test_create_handwriting_edit_existing_campaign(
self, delete_campaign_pages, enqueue_handwriting_generation, log_campaign_progress
):
# disconnected in the factory so we need to reconnect it here
signals.post_save.connect(
sender=Campaign,
dispatch_uid="apps.writing.signals.create_handwriting",
receiver=create_handwriting,
)
enqueue_handwriting_generation.return_value = True
log_campaign_progress.return_value = True
with self.captureOnCommitCallbacks(execute=True) as callbacks:
user = G(User)
campaign = G(Campaign, user=user)
assert Campaign.objects.get(pk=campaign.pk)
assert Campaign.objects.filter(pk=campaign.pk).exists()
enqueue_handwriting_generation.assert_called_with(campaign_pk=campaign.pk)
log_campaign_progress.assert_called_with(pk=campaign.pk, stage="campaign", status="t2h-edited")
delete_campaign_pages.assert_called_with(campaign_pk=campaign.pk)
django.test.TestCase
does not support transactions (there is a performance penalty with committing a database transaction and cleaning after test). As per https://docs.djangoproject.com/en/4.0/topics/testing/tools/#django.test.TestCase
- Wraps the tests within two nested atomic() blocks: one for the whole class and one for each test. Therefore, if you want to test some specific database transaction behavior, use
TransactionTestCase
.
You should use TransactionTestCase
https://docs.djangoproject.com/en/4.0/topics/testing/tools/#django.test.TransactionTestCase