It would be nice if pytest-django had on_commit support that didn't require a django_db(transaction=True) mark, something like this maybe.
https://medium.com/gitux/speed-up-django-transaction-hooks-tests-6de4a558ef96
For my particular usecase my task system submits tasks in on commit. So if we error out of a transaction we don't run any associated tasks.
In tests I have helpers to run some or all of the queued events.
In those helpers I also have an assert that fails when in a transaction, this exists to catch being in a test but no a transaction test, because I have on so many occasions spent so much time trying to work out where the hell my task has disappeared to.
It would be nice if there was a django_db variant that just ran the on_commit code at the same place it would run if transaction=True. It would also be nice if it had roughly equivalent performance as transaction=False so I could just blanket use it everywhere.
FWIW for testing a scenario where on_commit calls mytask.delay(), I was able to validate the task invocation by patching on_commit like so:
with mock.patch('mypackage.tasks.mytask.delay') as mock_task_delay:
with mock.patch('mypackage.models.transaction.on_commit', new=lambda fn: fn()):
mymodel.save()
assert mock_task_delay.call_count == 1
...
Since I created this issue I've also come across https://pypi.org/project/django-capture-on-commit-callbacks/
@trawick if you just want to validate that the call happens that is a simple and effective soution.
If you want to actually run the task code it has the downside that the task is scheduled immediately and so depending on how tasks are run in your tests it might fail intermittently if it depends on operations done later in the transaction.
Django 3.2 added TestCase.captureOnCommitCallbacks. It should be straightforward enough to add to pytest-django. I would use the django_assert_num_queries fixture as a template.
I would be happy for a PR if anyone would like to work on it!
Most helpful comment
FWIW for testing a scenario where
on_commitcallsmytask.delay(), I was able to validate the task invocation by patchingon_commitlike so: