Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

kingbuzzman
Copy link
Contributor

@kingbuzzman kingbuzzman commented Apr 13, 2025

Trac ticket number

ticket-36327

Branch description

The goal is to enhance the behavior of TestCase.assertNumQueries(). In large Django projects, the data structure is often split into multiple databases—for example, one for user data, another for business data, one for events, and even a dedicated one for certain monetary compliance logs. As a result, tests may need to validate query counts across several databases. For instance, you might have code that looks like this:

class TestThing(TestCase):
    databases = {"db1", "db2", "db3", "db4"}

    def test_thing(self):
        with self.assertNumQueries(4, using="db1"), self.assertNumQueries(0, using="db2"), self.assertNumQueries(1, using="db3"), self.assertNumQueries(0, using="db4"):
            thing()

The desired improvement is to simplify this syntax. Instead of writing a separate context manager for each database, you could pass multiple databases at once. For example:

class TestThing(TestCase):
    databases = {"db1", "db2", "db3", "db4"}

    def test_thing(self):
        with self.assertNumQueries(5, using={"db1", "db2", "db3", "db4"}):
            thing()

Checklist

  • This PR targets the main branch.
  • The commit message is written in past tense, mentions the ticket number, and ends with a period.
  • I have checked the "Has patch" ticket flag in the Trac system.
  • I have added or updated relevant tests.
  • I have added or updated relevant docs, including release notes if applicable.
  • I have attached screenshots in both light and dark modes for any UI changes.

@github-actions github-actions bot added the no ticket Based on PR title, no linked Trac ticket label Apr 13, 2025
@kingbuzzman kingbuzzman changed the title Adds ability to combine multiple connections into a single assert Ref #36327: Adds ability to combine multiple connections into a single assert Apr 14, 2025
@github-actions github-actions bot removed the no ticket Based on PR title, no linked Trac ticket label Apr 14, 2025
@adamchainz
Copy link
Member

Please retitle your commit and PR:

Fixed #36327 -- Added multiple database support to assertNumQueries().

https://docs.djangoproject.com/en/dev/internals/contributing/committing-code/#committing-guidelines

Copy link
Member

@adamchainz adamchainz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Quick fly-by review here, after your forum post about this draft

return results


class LazyExitStack:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why create this class? Afaict can make a plain ExitStack during _AssertNumQueriesContext.__enter__ instead.

Copy link
Contributor Author

@kingbuzzman kingbuzzman Apr 14, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will not work. ExitStack evaluates the __enter__ method right away. I added a test to demostrate what im trying to accomplish:

    def test_deferred(self):
        context = self.assertNumQueries(0)
        Person.objects.count()
        with context:
            pass

using ExitStack: (ps this was a quick and dirty example)

diff --git a/django/test/testcases.py b/django/test/testcases.py
index 8a3e47ed63..fa58471c88 100644
--- a/django/test/testcases.py
+++ b/django/test/testcases.py
@@ -102,14 +102,19 @@ class _AssertNumQueriesContext:
     def __init__(self, test_case, num, connections_):
         self.test_case = test_case
         self.num = num
-        self.cm = LazyExitStack()
+        # self.cm = LazyExitStack()
+        import contextlib
+        self.cm = contextlib.ExitStack()
+        self.cms = []
         for conn in connections_:
-            self.cm.enter_context(CaptureQueriesContext(conn))
+            context = CaptureQueriesContext(conn)
+            self.cms.append(context)
+            self.cm.enter_context(context)
 
     @property
     def captured_queries(self):
         return [
-            query for cm in self.cm.context_managers for query in cm.captured_queries
+            query for cm in self.cms for query in cm.captured_queries
         ]
 
     def __len__(self):

the tests fails:

======================================================================
FAIL: test_deferred (test_utils.tests.AssertNumQueriesContextManagerTests.test_deferred)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/django/source/tests/test_utils/tests.py", line 489, in test_deferred
    with context:
         ^^^^^^^
AssertionError: 1 != 0 : 1 queries executed, 0 expected
Captured queries were:
1. SELECT COUNT(*) AS "__count" FROM "test_utils_person"

----------------------------------------------------------------------
Ran 1 test in 0.004s

@kingbuzzman kingbuzzman changed the title Ref #36327: Adds ability to combine multiple connections into a single assert Fixed #36327 -- Added multiple database support to assertNumQueries() Apr 14, 2025
@kingbuzzman kingbuzzman requested a review from adamchainz April 14, 2025 17:12
@felixxm
Copy link
Member

felixxm commented Apr 14, 2025

Closing per ticket.

@felixxm felixxm closed this Apr 14, 2025
@kingbuzzman kingbuzzman deleted the dev/test-multi-db-assert-num branch April 14, 2025 20:13
"""

def __init__(self):
self.context_managers = []

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be a set.

Comment on lines +474 to +475
def enter_context(self, context_manager):
self.context_managers.append(context_manager)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

enter_context is a bad method name. You are registering a context_manager, not entering it. Better: register_context.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants