How to test a Django on_commit hook without clearing the database?

Adam Johnson wrote this, and I think the code referenced here does the trick:

https://adamj.eu/tech/2020/05/20/the-fast-way-to-test-django-transaction-on-commit-callbacks/

@contextmanager
    def captureOnCommitCallbacks(cls, *, using=DEFAULT_DB_ALIAS, execute=False):
        """Context manager to capture transaction.on_commit() callbacks."""
        callbacks = []
        start_count = len(connections[using].run_on_commit)
        try:
            yield callbacks
        finally:
            run_on_commit = connections[using].run_on_commit[start_count:]
            callbacks[:] = [func for sids, func in run_on_commit]
            if execute:
                for callback in callbacks:
                    callback()

usage:

class ContactTests(TestCase):
            def test_post(self):
                with self.captureOnCommitCallbacks(execute=True) as callbacks:
                    response = self.client.post(
                        '/contact/',
                        {'message': 'I like your site'},
                    )

                self.assertEqual(response.status_code, 200)
                self.assertEqual(len(callbacks), 1)

Starting with version 3.2 Django has a build-in way to test the on_comit hook. Example:

from django.core import mail
from django.test import TestCase


class ContactTests(TestCase):
    def test_post(self):
        with self.captureOnCommitCallbacks(execute=True) as callbacks:
            response = self.client.post(
                '/contact/',
                {'message': 'I like your site'},
            )

        self.assertEqual(response.status_code, 200)
        self.assertEqual(len(callbacks), 1)
        self.assertEqual(len(mail.outbox), 1)
        self.assertEqual(mail.outbox[0].subject, 'Contact Form')
        self.assertEqual(mail.outbox[0].body, 'I like your site')

Here is the official documentation: https://docs.djangoproject.com/en/stable/topics/testing/tools/#django.test.TestCase.captureOnCommitCallbacks


Just keep using TestCase and fake commit forcing executing of posponed actions in run_and_clear_commit_hooks. Check this article:

https://medium.com/gitux/speed-up-django-transaction-hooks-tests-6de4a558ef96