Skip to content

Commit 89595e3

Browse files
lukaszbblueyed
authored andcommitted
Add django_assert_num_queries fixture (#387)
1 parent 8b1d355 commit 89595e3

File tree

4 files changed

+108
-2
lines changed

4 files changed

+108
-2
lines changed

docs/helpers.rst

+20
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,26 @@ Example
218218
settings.USE_TZ = True
219219
assert settings.USE_TZ
220220

221+
222+
``django_assert_num_queries``
223+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
224+
225+
This fixture allows to check for an expected number of DB queries.
226+
It currently only supports the default database.
227+
228+
229+
Example
230+
"""""""
231+
232+
::
233+
234+
def test_queries(assert_num_queries):
235+
with django_assert_num_queries(3):
236+
Item.objects.create('foo')
237+
Item.objects.create('bar')
238+
Item.objects.create('baz')
239+
240+
221241
``mailoutbox``
222242
~~~~~~~~~~~~~~~~~~~~~~~~~
223243

pytest_django/fixtures.py

+24-1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66

77
import pytest
88

9+
from contextlib import contextmanager
10+
911
from . import live_server_helper
1012

1113
from .django_compat import is_django_unittest
@@ -16,7 +18,7 @@
1618
__all__ = ['django_db_setup', 'db', 'transactional_db', 'admin_user',
1719
'django_user_model', 'django_username_field',
1820
'client', 'admin_client', 'rf', 'settings', 'live_server',
19-
'_live_server_helper']
21+
'_live_server_helper', 'django_assert_num_queries']
2022

2123

2224
@pytest.fixture(scope='session')
@@ -339,3 +341,24 @@ def _live_server_helper(request):
339341
"""
340342
if 'live_server' in request.funcargnames:
341343
getfixturevalue(request, 'transactional_db')
344+
345+
346+
@pytest.fixture(scope='function')
347+
def django_assert_num_queries(pytestconfig):
348+
from django.db import connection
349+
from django.test.utils import CaptureQueriesContext
350+
351+
@contextmanager
352+
def _assert_num_queries(num):
353+
with CaptureQueriesContext(connection) as context:
354+
yield
355+
if num != len(context):
356+
msg = "Expected to perform %s queries but %s were done" % (num, len(context))
357+
if pytestconfig.getoption('verbose') > 0:
358+
sqls = (q['sql'] for q in context.captured_queries)
359+
msg += '\n\nQueries:\n========\n\n%s' % '\n\n'.join(sqls)
360+
else:
361+
msg += " (add -v option to show queries)"
362+
pytest.fail(msg)
363+
364+
return _assert_num_queries

pytest_django/plugin.py

+1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import pytest
1616

1717
from .django_compat import is_django_unittest # noqa
18+
from .fixtures import django_assert_num_queries # noqa
1819
from .fixtures import django_db_setup # noqa
1920
from .fixtures import django_db_use_migrations # noqa
2021
from .fixtures import django_db_keepdb # noqa

tests/test_fixtures.py

+63-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
import pytest
1010

11-
from django.db import connection
11+
from django.db import connection, transaction
1212
from django.conf import settings as real_settings
1313
from django.core import mail
1414
from django.test.client import Client, RequestFactory
@@ -50,6 +50,68 @@ def test_rf(rf):
5050
assert isinstance(rf, RequestFactory)
5151

5252

53+
@pytest.mark.django_db
54+
def test_django_assert_num_queries_db(django_assert_num_queries):
55+
with django_assert_num_queries(3):
56+
Item.objects.create(name='foo')
57+
Item.objects.create(name='bar')
58+
Item.objects.create(name='baz')
59+
60+
with pytest.raises(pytest.fail.Exception):
61+
with django_assert_num_queries(2):
62+
Item.objects.create(name='quux')
63+
64+
65+
@pytest.mark.django_db(transaction=True)
66+
def test_django_assert_num_queries_transactional_db(transactional_db, django_assert_num_queries):
67+
with transaction.atomic():
68+
69+
with django_assert_num_queries(3):
70+
Item.objects.create(name='foo')
71+
Item.objects.create(name='bar')
72+
Item.objects.create(name='baz')
73+
74+
with pytest.raises(pytest.fail.Exception):
75+
with django_assert_num_queries(2):
76+
Item.objects.create(name='quux')
77+
78+
79+
def test_django_assert_num_queries_output(django_testdir):
80+
django_testdir.create_test_module("""
81+
from django.contrib.contenttypes.models import ContentType
82+
import pytest
83+
84+
@pytest.mark.django_db
85+
def test_queries(django_assert_num_queries):
86+
with django_assert_num_queries(1):
87+
list(ContentType.objects.all())
88+
ContentType.objects.count()
89+
""")
90+
result = django_testdir.runpytest_subprocess('--tb=short')
91+
result.stdout.fnmatch_lines(['*Expected to perform 1 queries but 2 were done*'])
92+
assert result.ret == 1
93+
94+
95+
def test_django_assert_num_queries_output_verbose(django_testdir):
96+
django_testdir.create_test_module("""
97+
from django.contrib.contenttypes.models import ContentType
98+
import pytest
99+
100+
@pytest.mark.django_db
101+
def test_queries(django_assert_num_queries):
102+
with django_assert_num_queries(11):
103+
list(ContentType.objects.all())
104+
ContentType.objects.count()
105+
""")
106+
result = django_testdir.runpytest_subprocess('--tb=short', '-v')
107+
result.stdout.fnmatch_lines([
108+
'*Expected to perform 11 queries but 2 were done*',
109+
'*Queries:*',
110+
'*========*',
111+
])
112+
assert result.ret == 1
113+
114+
53115
class TestSettings:
54116
"""Tests for the settings fixture, order matters"""
55117

0 commit comments

Comments
 (0)