#30457: on_commit should be triggered in a TestCase
------------------------------------+--------------------------------------
     Reporter:  Bernhard Mäder      |                    Owner:  nobody
         Type:  New feature         |                   Status:  new
    Component:  Testing framework   |                  Version:  master
     Severity:  Normal              |               Resolution:
     Keywords:  on_commit TestCase  |             Triage Stage:  Unreviewed
    Has patch:  0                   |      Needs documentation:  0
  Needs tests:  0                   |  Patch needs improvement:  0
Easy pickings:  0                   |                    UI/UX:  0
------------------------------------+--------------------------------------
Changes (by Simon Charette):

 * cc: Simon Charette (added)
 * version:  1.11 => master


Comment:

 Hi Bernhard,

 `on_commit` hooks are not run during `TestCase` because of the transaction
 wrapping it performs to ensure data isolation. In short each test is
 wrapped in a transaction that is rolled back on teardown
 [https://docs.djangoproject.com/en/2.2/topics/testing/tools/#testcase
 which is documented]. That means the connection never actually commits the
 transaction and thus `on_commit` hooks are never fired. I've previously
 discussed changing the behavior to make `on_commit` hook fire on the most
 inner `SAVEPOINT` commit to abstract's `TestCase` wrapping but that would
 be backward incompatible and wouldn't account for nested
 `atomic(savepoint=False)` usages.

 The only viable solution we came up with at $DAYJOB is similar to yours.
 We added an `immediate_on_commit` context manager to our base `TestCase`
 subclass

 {{{#!python
 @contextmanager
 def immediate_on_commit(self, using=None):
     """
     Context manager executing transaction.on_commit() hooks immediately as
     if the connection was in auto-commit mode. This is required when
     using a subclass of django.test.TestCase as all tests are wrapped in
     a transaction that never gets committed.
     """
     immediate_using = DEFAULT_DB_ALIAS if using is None else using
     def on_commit(func, using=None):
         using = DEFAULT_DB_ALIAS if using is None else using
         if using == immediate_using:
             func()
     with mock.patch('django.db.transaction.on_commit',
 side_effect=on_commit) as patch:
         yield patch
 }}}

 This also allows us to assert against the `on_commit` mock's
 `assert_called_*` methods.

 Right now the proper way to test for `on_commit` execution is to use a
 `TransactionTestCase` which doesn't performs this transaction wrapping.

 I guess the documentation of `TestCase`, `TransactionTestCase` and
 `on_commit` could be enhanced in this regard and a solution similar to
 `immediate_on_commit` could be added to `TestCase`.

 Thoughts?

-- 
Ticket URL: <https://code.djangoproject.com/ticket/30457#comment:1>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.

-- 
You received this message because you are subscribed to the Google Groups 
"Django updates" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To post to this group, send email to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/django-updates/064.6d0d6629be302a06d77c5ab6e2758c11%40djangoproject.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to