#33277: SimpleTestCase does not block database connections in threads
---------------------------------------------+------------------------
               Reporter:  Daniel Hahler      |          Owner:  nobody
                   Type:  Uncategorized      |         Status:  new
              Component:  Testing framework  |        Version:  3.2
               Severity:  Normal             |       Keywords:
           Triage Stage:  Unreviewed         |      Has patch:  0
    Needs documentation:  0                  |    Needs tests:  0
Patch needs improvement:  0                  |  Easy pickings:  0
                  UI/UX:  0                  |
---------------------------------------------+------------------------
 Due to {{{ConnectionHandler}}}'s connections being thread-local [1] new
 connections will be used in new threads, which then do not have been
 patched for disallowed methods [2].

 Given {{{test_simpletestcase.py}}}:

 {{{
 import threading

 from django.db import connection
 from django.test import SimpleTestCase


 class MySimpleTestCase(SimpleTestCase):
     def test_this(self):
         try:
             with connection.cursor() as cursor:
                 cursor.execute("SELECT 1")
             raise Exception("should have failed")
         except AssertionError:
             pass

         res = []

         def thread_func():
             res.append(1)
             try:
                 with connection.cursor() as cursor:
                     cursor.execute("SELECT 1")
                 raise Exception("should have failed")
             except AssertionError:
                 pass
             res.append(2)

         t = threading.Thread(target=thread_func)
         t.start()
         t.join()
         assert res == [1, 2], res
 }}}

 {{{./manage.py test test_simpletestcase.py}}} fails like this:
 {{{
 Exception in thread Thread-1:
 Traceback (most recent call last):
   File "/usr/lib/python3.9/threading.py", line 973, in _bootstrap_inner
     self.run()
   File "/usr/lib/python3.9/threading.py", line 910, in run
     self._target(*self._args, **self._kwargs)
   File "…/test_simpletestcase.py", line 23, in thread_func
     raise Exception("should have failed")
 Exception: should have failed
 F
 ======================================================================
 FAIL: test_this (test_simpletestcase.MySimpleTestCase)
 ----------------------------------------------------------------------
 Traceback (most recent call last):
   File "…/test_simpletestcase.py", line 31, in test_this
     assert res == [1, 2], res
 AssertionError: [1]

 ----------------------------------------------------------------------
 Ran 1 test in 0.006s

 FAILED (failures=1)
 }}}

 (Note that there is some handling of {{{connection.settings_dict}}} for
 workers of the test runner, which is only slightly related:
 
https://github.com/django/django/blob/dfa1145a22042dcf9e504a5a7edd5557e3e0d07c/django/test/runner.py#L327-L335)

 A possible solution might be to use the existing
 [https://docs.djangoproject.com/en/3.2/ref/signals/#connection-created
 connection_created] signal to raise an exception when a connections was
 created (although that would happen only after the fact - a new pre-
 connect signal could be used/added for this).

 Given that the test DB names are not prefixed with {{{test_}}} with
 {{{SimpleTestCase}}} you might accidentally change the production DB from
 within your tests when something like a {{{ThreadPoolExecutor}}} is being
 used when mixing sync with async etc.

 1:
 
https://github.com/django/django/blob/dfa1145a22042dcf9e504a5a7edd5557e3e0d07c/django/utils/connection.py#L41
 2:
 
https://github.com/django/django/blob/dfa1145a22042dcf9e504a5a7edd5557e3e0d07c/django/test/testcases.py#L183

-- 
Ticket URL: <https://code.djangoproject.com/ticket/33277>
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 view this discussion on the web visit 
https://groups.google.com/d/msgid/django-updates/050.e51111039e1e939ee69e934a55272aa1%40djangoproject.com.

Reply via email to