Тестирование сигнала django `post_save`, включающего вызовы функций, которые происходят после фиксации транзакции db

Когда выполняются тесты django, транзакции базы данных не фиксируются. Как мне протестировать событие, вызванное созданием объекта, но происходящее после фиксации транзакции базы данных?

У меня есть модель Campaign и приведенный ниже сигнал post_save. Используя Django TestCase, трудно утверждать, что функции внутри transaction.on_commit вызываются в случае, когда создается новый объект Campaign. Когда сигнал выполняется в тестовом контексте, он всегда думает, что редактируется существующий объект кампании, а не что он был создан заново. Поэтому я не могу протестировать ветвь else оператора if.

Как я могу проверить случай, когда Campaign.objects.filter(pk=instance.pk).exists() равно False?

Сигнал:

@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))

Тест:

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 не поддерживает транзакции (существует штраф за производительность при фиксации транзакции базы данных и очистке после тестирования). В соответствии с https://docs.djangoproject.com/en/4.0/topics/testing/tools/#django.test.TestCase

  • Завертывает тесты в два вложенных блока atomic(): один для всего класса и один для каждого теста. Поэтому, если вы хотите протестировать определенное поведение транзакции базы данных, используйте TransactionTestCase.

Вам следует использовать TransactionTestCase https://docs.djangoproject.com/en/4.0/topics/testing/tools/#django.test.TransactionTestCase

Вернуться на верх