Skip to content
Merged
18 changes: 18 additions & 0 deletions docs/helpers.rst
Original file line number Diff line number Diff line change
Expand Up @@ -217,3 +217,21 @@ Example
def test_with_specific_settings(settings):
settings.USE_TZ = True
assert settings.USE_TZ

``assert_num_queries``
~~~~~~~~~~~~~~~~~~~~~~

This fixture allows to check that expected number of queries are performed.
Copy link
Contributor

Choose a reason for hiding this comment

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

s/of queries/of DB queries/

Note that currently it only supports default database.
Copy link
Contributor

Choose a reason for hiding this comment

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

better:

Note that it currently only supports the default database.



Example
"""""""

::

def test_queries(assert_num_queries):
with assert_num_queries(3):
Copy link
Contributor

Choose a reason for hiding this comment

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

s/assert_num_queries/django_assert_num_queries/

Item.objects.create('foo')
Item.objects.create('bar')
Item.objects.create('baz')
25 changes: 24 additions & 1 deletion pytest_django/fixtures.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@

import pytest

from contextlib import contextmanager

from . import live_server_helper

from .django_compat import is_django_unittest
Expand All @@ -15,7 +17,7 @@
__all__ = ['django_db_setup', 'db', 'transactional_db', 'admin_user',
'django_user_model', 'django_username_field',
'client', 'admin_client', 'rf', 'settings', 'live_server',
'_live_server_helper']
'_live_server_helper', 'assert_num_queries']


@pytest.fixture(scope='session')
Expand Down Expand Up @@ -323,3 +325,24 @@ def _live_server_helper(request):
"""
if 'live_server' in request.funcargnames:
request.getfuncargvalue('transactional_db')


@pytest.fixture(scope='function')
def assert_num_queries(pytestconfig):
from django.db import connection
from django.test.utils import CaptureQueriesContext

@contextmanager
def _assert_num_queries(num):
with CaptureQueriesContext(connection) as context:
yield
msg = "Expected to perform %s queries but %s were done" % (num, len(context))
if pytestconfig.getoption('verbose') > 0:
sqls = (q['sql'] for q in context.captured_queries)
msg += '\n\nQueries:\n========\n\n%s' % '\n\n'.join(sqls)
else:
msg += " (add -v option to show queries)"
if num != len(context):
pytest.fail(msg)

return _assert_num_queries
1 change: 1 addition & 0 deletions pytest_django/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
from .fixtures import _live_server_helper # noqa
from .fixtures import admin_client # noqa
from .fixtures import admin_user # noqa
from .fixtures import assert_num_queries # noqa
from .fixtures import client # noqa
from .fixtures import db # noqa
from .fixtures import django_user_model # noqa
Expand Down
64 changes: 63 additions & 1 deletion tests/test_fixtures.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

import pytest

from django.db import connection
from django.db import connection, transaction
from django.conf import settings as real_settings
from django.test.client import Client, RequestFactory
from django.test.testcases import connections_support_transactions
Expand Down Expand Up @@ -49,6 +49,68 @@ def test_rf(rf):
assert isinstance(rf, RequestFactory)


@pytest.mark.django_db
def test_assert_num_queries_db(assert_num_queries):
with assert_num_queries(3):
Item.objects.create(name='foo')
Item.objects.create(name='bar')
Item.objects.create(name='baz')

with pytest.raises(pytest.fail.Exception):
with assert_num_queries(2):
Item.objects.create(name='quux')


@pytest.mark.django_db(transaction=True)
def test_assert_num_queries_transactional_db(transactional_db, assert_num_queries):
with transaction.atomic():

with assert_num_queries(3):
Item.objects.create(name='foo')
Item.objects.create(name='bar')
Copy link
Contributor

Choose a reason for hiding this comment

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

Please test for the expected output here, using testdir.runpytest_subprocess (see other tests for examples).

Item.objects.create(name='baz')

with pytest.raises(pytest.fail.Exception):
with assert_num_queries(2):
Item.objects.create(name='quux')


def test_assert_num_queries_output(django_testdir):
django_testdir.create_test_module("""
from django.contrib.contenttypes.models import ContentType
import pytest

@pytest.mark.django_db
def test_queries(assert_num_queries):
with assert_num_queries(1):
list(ContentType.objects.all())
ContentType.objects.count()
""")
result = django_testdir.runpytest_subprocess('--tb=short')
result.stdout.fnmatch_lines(['*Expected to perform 1 queries but 2 were done*'])
assert result.ret == 1


def test_assert_num_queries_output_verbose(django_testdir):
django_testdir.create_test_module("""
from django.contrib.contenttypes.models import ContentType
import pytest

@pytest.mark.django_db
def test_queries(assert_num_queries):
with assert_num_queries(11):
list(ContentType.objects.all())
ContentType.objects.count()
""")
result = django_testdir.runpytest_subprocess('--tb=short', '-v')
result.stdout.fnmatch_lines([
'*Expected to perform 11 queries but 2 were done*',
'*Queries:*',
'*========*',
])
assert result.ret == 1


class TestSettings:
"""Tests for the settings fixture, order matters"""

Expand Down