Skip to content

Commit d6c6033

Browse files
committed
Merge pull request #547 from anrie/feat/max_num_queries
Closes #547.
2 parents 8d0bde0 + 7a655f8 commit d6c6033

File tree

5 files changed

+119
-22
lines changed

5 files changed

+119
-22
lines changed

docs/changelog.rst

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,16 @@
11
Changelog
22
=========
33

4+
unreleased
5+
------------------
6+
7+
Features
8+
^^^^^^^^
9+
10+
* Added new fixture :fixture:`django_assert_max_num_queries` (#547).
11+
* Assed support for ``connection`` and returning the wrapped context manager
12+
with :fixture:`django_assert_num_queries` (#547).
13+
414
3.3.3 (2018-07-26)
515
------------------
616

docs/helpers.rst

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -249,8 +249,13 @@ Example
249249
``django_assert_num_queries``
250250
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
251251

252+
.. fixture:: django_assert_num_queries
253+
252254
This fixture allows to check for an expected number of DB queries.
253-
It currently only supports the default database.
255+
256+
It wraps `django.test.utils.CaptureQueriesContext`. A non-default DB
257+
connection can be passed in using the `connection` keyword argument, and it
258+
will yield the wrapped CaptureQueriesContext instance.
254259

255260

256261
Example
@@ -259,11 +264,34 @@ Example
259264
::
260265

261266
def test_queries(django_assert_num_queries):
262-
with django_assert_num_queries(3):
267+
with django_assert_num_queries(3) as captured:
263268
Item.objects.create('foo')
264269
Item.objects.create('bar')
265270
Item.objects.create('baz')
266271

272+
assert 'foo' in captured.captured_queries[0]['sql']
273+
274+
275+
``django_assert_max_num_queries``
276+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
277+
278+
.. fixture:: django_assert_max_num_queries
279+
280+
This fixture allows to check for an expected maximum number of DB queries.
281+
282+
It is a specialized version of :fixture:`django_assert_num_queries`.
283+
284+
285+
Example
286+
"""""""
287+
288+
::
289+
290+
def test_max_queries(django_assert_max_num_queries):
291+
with django_assert_max_num_queries(3):
292+
Item.objects.create('foo')
293+
Item.objects.create('bar')
294+
267295

268296
``mailoutbox``
269297
~~~~~~~~~~~~~~

pytest_django/fixtures.py

Lines changed: 36 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
from __future__ import with_statement
44

55
import os
6+
from functools import partial
67

78
import pytest
89

@@ -17,7 +18,8 @@
1718
__all__ = ['django_db_setup', 'db', 'transactional_db', 'admin_user',
1819
'django_user_model', 'django_username_field',
1920
'client', 'admin_client', 'rf', 'settings', 'live_server',
20-
'_live_server_helper', 'django_assert_num_queries']
21+
'_live_server_helper', 'django_assert_num_queries',
22+
'django_assert_max_num_queries']
2123

2224

2325
@pytest.fixture(scope='session')
@@ -352,22 +354,39 @@ def _live_server_helper(request):
352354
request.addfinalizer(live_server._live_server_modified_settings.disable)
353355

354356

357+
@contextmanager
358+
def _assert_num_queries(config, num, exact=True, connection=None):
359+
from django.test.utils import CaptureQueriesContext
360+
361+
if connection is None:
362+
from django.db import connection
363+
364+
verbose = config.getoption('verbose') > 0
365+
with CaptureQueriesContext(connection) as context:
366+
yield context
367+
num_queries = len(context)
368+
failed = num != num_queries if exact else num < num_queries
369+
if failed:
370+
msg = "Expected to perform {} queries {}{}".format(
371+
num,
372+
'' if exact else 'or less ',
373+
'but {} done'.format(
374+
num_queries == 1 and '1 was' or '%d were' % (num_queries,)
375+
)
376+
)
377+
if verbose:
378+
sqls = (q['sql'] for q in context.captured_queries)
379+
msg += '\n\nQueries:\n========\n\n%s' % '\n\n'.join(sqls)
380+
else:
381+
msg += " (add -v option to show queries)"
382+
pytest.fail(msg)
383+
384+
355385
@pytest.fixture(scope='function')
356386
def django_assert_num_queries(pytestconfig):
357-
from django.db import connection
358-
from django.test.utils import CaptureQueriesContext
387+
return partial(_assert_num_queries, pytestconfig)
359388

360-
@contextmanager
361-
def _assert_num_queries(num):
362-
with CaptureQueriesContext(connection) as context:
363-
yield
364-
if num != len(context):
365-
msg = "Expected to perform %s queries but %s were done" % (num, len(context))
366-
if pytestconfig.getoption('verbose') > 0:
367-
sqls = (q['sql'] for q in context.captured_queries)
368-
msg += '\n\nQueries:\n========\n\n%s' % '\n\n'.join(sqls)
369-
else:
370-
msg += " (add -v option to show queries)"
371-
pytest.fail(msg)
372-
373-
return _assert_num_queries
389+
390+
@pytest.fixture(scope='function')
391+
def django_assert_max_num_queries(pytestconfig):
392+
return partial(_assert_num_queries, pytestconfig, exact=False)

pytest_django/plugin.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
from .django_compat import is_django_unittest # noqa
1818
from .fixtures import django_assert_num_queries # noqa
19+
from .fixtures import django_assert_max_num_queries # noqa
1920
from .fixtures import django_db_setup # noqa
2021
from .fixtures import django_db_use_migrations # noqa
2122
from .fixtures import django_db_keepdb # noqa

tests/test_fixtures.py

Lines changed: 42 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,13 +59,37 @@ def test_django_assert_num_queries_db(django_assert_num_queries):
5959
Item.objects.create(name='bar')
6060
Item.objects.create(name='baz')
6161

62-
with pytest.raises(pytest.fail.Exception):
63-
with django_assert_num_queries(2):
62+
with pytest.raises(pytest.fail.Exception) as excinfo:
63+
with django_assert_num_queries(2) as captured:
6464
Item.objects.create(name='quux')
65+
assert excinfo.value.args == (
66+
'Expected to perform 2 queries but 1 was done '
67+
'(add -v option to show queries)',)
68+
assert len(captured.captured_queries) == 1
69+
70+
71+
@pytest.mark.django_db
72+
def test_django_assert_max_num_queries_db(django_assert_max_num_queries):
73+
with django_assert_max_num_queries(2):
74+
Item.objects.create(name='1-foo')
75+
Item.objects.create(name='2-bar')
76+
77+
with pytest.raises(pytest.fail.Exception) as excinfo:
78+
with django_assert_max_num_queries(2) as captured:
79+
Item.objects.create(name='1-foo')
80+
Item.objects.create(name='2-bar')
81+
Item.objects.create(name='3-quux')
82+
83+
assert excinfo.value.args == (
84+
'Expected to perform 2 queries or less but 3 were done '
85+
'(add -v option to show queries)',)
86+
assert len(captured.captured_queries) == 3
87+
assert '1-foo' in captured.captured_queries[0]['sql']
6588

6689

6790
@pytest.mark.django_db(transaction=True)
68-
def test_django_assert_num_queries_transactional_db(transactional_db, django_assert_num_queries):
91+
def test_django_assert_num_queries_transactional_db(
92+
transactional_db, django_assert_num_queries):
6993
with transaction.atomic():
7094

7195
with django_assert_num_queries(3):
@@ -114,6 +138,21 @@ def test_queries(django_assert_num_queries):
114138
assert result.ret == 1
115139

116140

141+
@pytest.mark.django_db
142+
def test_django_assert_num_queries_db_connection(django_assert_num_queries):
143+
from django.db import connection
144+
145+
with django_assert_num_queries(1, connection=connection):
146+
Item.objects.create(name='foo')
147+
148+
with django_assert_num_queries(1, connection=None):
149+
Item.objects.create(name='foo')
150+
151+
with pytest.raises(AttributeError):
152+
with django_assert_num_queries(1, connection=False):
153+
pass
154+
155+
117156
class TestSettings:
118157
"""Tests for the settings fixture, order matters"""
119158

0 commit comments

Comments
 (0)