Opened 3 years ago
Last modified 12 months ago
#33277 closed New feature
SimpleTestCase does not block database connections in threads — at Version 1
Reported by: | Daniel Hahler | Owned by: | nobody |
---|---|---|---|
Component: | Testing framework | Version: | 3.2 |
Severity: | Normal | Keywords: | |
Cc: | David Wobrock | Triage Stage: | Ready for checkin |
Has patch: | yes | Needs documentation: | no |
Needs tests: | no | Patch needs improvement: | no |
Easy pickings: | no | UI/UX: | no |
Description (last modified by )
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 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.
Note: pytest-django monkeypatches django.db.backends.base.base.BaseDatabaseWrapper.ensure_connection
to block DB access, which appears to work better in this regard (across threads).
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