I have a large Django application with tons of tests that need SQL query optimizations.
I'm using pytest-django to run my tests.
I don't want to add assertNumQueries
or django-assert-num-queries
for each test individually, but instead produce an overview about how many SQL queries each one of all of the tests are triggering, to know which code needs the most optimization, like this:
test | number of queries
------------------------------------------------
test_a.py::test_my_function1 | 5
test_a.py::test_my_function3 | 2
test_a.py::test_my_function5 | 7
Is it possible to configure a pytest hook in conftest.py which counts the number of SQL queries for each (DB) test and shows them in result - without the need to modify the source of my tests (like adding decorators)?
My naive approach would be to use these hooks, and somehow get access to the database connection before and after each test:
def pytest_runtest_call(item):
pass
def pytest_runtest_teardown(item, nextitem):
return True
Only possible way is to actually execute select COUNT(*) from view_name , because views can contain complicated logic and the data returned by them actually is allocated to other objects (e.g. tables). For tables for example, you can get this rows count from sys. partitions DMV.
In SQL, you can make a database query and use the COUNT function to get the number of rows for a particular group in the table. Here is the basic syntax: SELECT COUNT(column_name) FROM table_name; COUNT(column_name) will not include NULL values as part of the count.
The COUNT() function returns the number of rows that matches a specified criterion.
For recording the queries count, an autouse fixture will suffice. In the below example, I store the count in a queries_count
dict under the config object:
@pytest.fixture(autouse=True)
def record_query_count(request):
from django.test.utils import CaptureQueriesContext
from django.db import connection
with CaptureQueriesContext(connection) as context:
yield
num_performed = len(context)
if not hasattr(request.config, "query_counts"):
request.config.query_counts = dict()
request.config.query_counts[request.node.nodeid] = num_performed
To output the results, I add a custom section in a custom impl of the pytest_terminal_summary
hook. Put the following code in a file named conftest.py
in the project or tests root dir:
import os
def pytest_terminal_summary(terminalreporter, exitstatus, config):
content = os.linesep.join(
f'{nodeid:40} | {num_queries}'
for nodeid, num_queries in config.query_counts.items()
)
terminalreporter.ensure_newline()
terminalreporter.section('number of queries', sep='-', bold=True)
terminalreporter.line(content)
Running the tests will now yield:
tests/test_db.py ... [100%]
----------------------------------- number of queries ------------------------------------
tests/test_db.py::test_add_entry | 2
tests/test_db.py::test_remove_entry | 2
tests/test_db.py::test_no_entries | 1
=================================== 3 passed in 0.26s ====================================
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With