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