test_classes: Create a dedicate helper for query count check.

This adds a helper based on testing patterns of using the "queries_captured"
context manager with "assert_length" to check the number of queries
executed for preventing performance regression.

It explains the rationale of checking the query count through an
"AssertionError" and prints the queries captured as assert_length does,
but with a format optimized for displaying the queries in a more
readable manner.

Signed-off-by: Zixuan James Li <p359101898@gmail.com>
This commit is contained in:
Zixuan James Li
2022-10-15 16:47:40 -04:00
committed by Tim Abbott
parent 9117db8887
commit 46329a2710
21 changed files with 122 additions and 200 deletions

View File

@@ -70,7 +70,7 @@ from zerver.lib.test_console_output import (
tee_stderr_and_find_extra_console_output,
tee_stdout_and_find_extra_console_output,
)
from zerver.lib.test_helpers import find_key_by_email, instrument_url
from zerver.lib.test_helpers import find_key_by_email, instrument_url, queries_captured
from zerver.lib.topic import filter_by_topic_name_via_message
from zerver.lib.user_groups import get_system_user_group_for_user
from zerver.lib.users import get_api_key
@@ -1123,6 +1123,33 @@ Output:
print(f"\nexpected length: {count}\nactual length: {actual_count}")
raise AssertionError(f"{str(type(items))} is of unexpected size!")
@contextmanager
def assert_database_query_count(
self, count: int, include_savepoints: bool = False, keep_cache_warm: bool = False
) -> Iterator[None]:
"""
This captures the queries executed and check the total number of queries.
Useful when minimizing unnecessary roundtrips to the database is important.
"""
with queries_captured(
include_savepoints=include_savepoints, keep_cache_warm=keep_cache_warm
) as queries:
yield
actual_count = len(queries)
if actual_count != count: # nocoverage
print("\nITEMS:\n")
for index, query in enumerate(queries):
print(f"#{index + 1}\nsql: {str(query['sql'])}\ntime: {query['time']}\n")
print(f"expected count: {count}\nactual count: {actual_count}")
raise AssertionError(
f"""
{count} queries expected, got {actual_count}.
This is a performance-critical code path, where we check
the number of database queries used in order to avoid accidental regressions.
If the new query is necessary, you should update this test,
and explain in the pull request why adding an additional query can't be avoided."""
)
def assert_json_error_contains(
self, result: "TestHttpResponse", msg_substring: str, status_code: int = 400
) -> None: