From 76548c87072a9a3dcfd0eb8aa4f2b86be8f9a9e9 Mon Sep 17 00:00:00 2001 From: Lukasz Balcerzak Date: Fri, 26 Aug 2016 09:40:48 +0200 Subject: [PATCH 01/11] Added assert_num_queries fixture --- docs/helpers.rst | 18 ++++++++++++++++++ pytest_django/fixtures.py | 24 +++++++++++++++++++++++- pytest_django/plugin.py | 1 + tests/test_fixtures.py | 16 ++++++++++++++++ 4 files changed, 58 insertions(+), 1 deletion(-) diff --git a/docs/helpers.rst b/docs/helpers.rst index 68afd6ded..a1eb3a17e 100644 --- a/docs/helpers.rst +++ b/docs/helpers.rst @@ -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. +Note that currently it only supports default database. + + +Example +""""""" + +:: + + def test_queries(assert_num_queries): + with assert_num_queries(3): + Item.objects.create('foo') + Item.objects.create('bar') + Item.objects.create('baz') diff --git a/pytest_django/fixtures.py b/pytest_django/fixtures.py index 97caa84dd..973b8b96a 100644 --- a/pytest_django/fixtures.py +++ b/pytest_django/fixtures.py @@ -6,6 +6,10 @@ import pytest +from contextlib import contextmanager +from django.db import connection +from django.test.utils import CaptureQueriesContext + from . import live_server_helper from .django_compat import is_django_unittest @@ -15,7 +19,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') @@ -323,3 +327,21 @@ def _live_server_helper(request): """ if 'live_server' in request.funcargnames: request.getfuncargvalue('transactional_db') + + +@pytest.fixture(scope='function') +def assert_num_queries(db, pytestconfig): + + @contextmanager + def _assert_num_queries(num): + with CaptureQueriesContext(connection) as context: + yield + msg = "Expected to perform %s queries but %s was 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)" + assert len(context) == num, msg + + return _assert_num_queries diff --git a/pytest_django/plugin.py b/pytest_django/plugin.py index 2e9941b03..1e2a3c505 100644 --- a/pytest_django/plugin.py +++ b/pytest_django/plugin.py @@ -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 diff --git a/tests/test_fixtures.py b/tests/test_fixtures.py index af814e560..4e990a0ab 100644 --- a/tests/test_fixtures.py +++ b/tests/test_fixtures.py @@ -49,6 +49,22 @@ def test_rf(rf): assert isinstance(rf, RequestFactory) +@pytest.mark.django_db +def test_assert_num_queries(assert_num_queries, django_user_model): + with assert_num_queries(3): + Item.objects.create(name='foo') + Item.objects.create(name='bar') + Item.objects.create(name='baz') + + +@pytest.mark.django_db +def test_assert_num_queries_fails_properly(assert_num_queries, django_user_model): + with pytest.raises(AssertionError): + with assert_num_queries(3): + Item.objects.create(name='foo') + Item.objects.create(name='bar') + + class TestSettings: """Tests for the settings fixture, order matters""" From 4f2875e985ad3d4c9ab8f5242cf8fba09f6373ae Mon Sep 17 00:00:00 2001 From: Lukasz Balcerzak Date: Wed, 31 Aug 2016 09:04:31 +0200 Subject: [PATCH 02/11] move imports --- pytest_django/fixtures.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pytest_django/fixtures.py b/pytest_django/fixtures.py index 973b8b96a..f1834eed7 100644 --- a/pytest_django/fixtures.py +++ b/pytest_django/fixtures.py @@ -7,8 +7,6 @@ import pytest from contextlib import contextmanager -from django.db import connection -from django.test.utils import CaptureQueriesContext from . import live_server_helper @@ -331,6 +329,8 @@ def _live_server_helper(request): @pytest.fixture(scope='function') def assert_num_queries(db, pytestconfig): + from django.db import connection + from django.test.utils import CaptureQueriesContext @contextmanager def _assert_num_queries(num): From f87689d1b85f0977b7517a7d69dfebf1a70a5e8d Mon Sep 17 00:00:00 2001 From: Lukasz Balcerzak Date: Wed, 31 Aug 2016 09:04:36 +0200 Subject: [PATCH 03/11] typo --- pytest_django/fixtures.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pytest_django/fixtures.py b/pytest_django/fixtures.py index f1834eed7..e850c54ce 100644 --- a/pytest_django/fixtures.py +++ b/pytest_django/fixtures.py @@ -336,7 +336,7 @@ def assert_num_queries(db, pytestconfig): def _assert_num_queries(num): with CaptureQueriesContext(connection) as context: yield - msg = "Expected to perform %s queries but %s was done" % (num, len(context)) + 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) From 4a23b165733af2af54a82636f221af8f3fc3bc5f Mon Sep 17 00:00:00 2001 From: Lukasz Balcerzak Date: Wed, 31 Aug 2016 09:16:27 +0200 Subject: [PATCH 04/11] use pytest.fail instead of assert --- pytest_django/fixtures.py | 3 ++- tests/test_fixtures.py | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/pytest_django/fixtures.py b/pytest_django/fixtures.py index e850c54ce..3b6a32b28 100644 --- a/pytest_django/fixtures.py +++ b/pytest_django/fixtures.py @@ -342,6 +342,7 @@ def _assert_num_queries(num): msg += '\n\nQueries:\n========\n\n%s' % '\n\n'.join(sqls) else: msg += " (add -v option to show queries)" - assert len(context) == num, msg + if num != len(context): + pytest.fail(msg) return _assert_num_queries diff --git a/tests/test_fixtures.py b/tests/test_fixtures.py index 4e990a0ab..0754b346c 100644 --- a/tests/test_fixtures.py +++ b/tests/test_fixtures.py @@ -59,7 +59,7 @@ def test_assert_num_queries(assert_num_queries, django_user_model): @pytest.mark.django_db def test_assert_num_queries_fails_properly(assert_num_queries, django_user_model): - with pytest.raises(AssertionError): + with pytest.raises(pytest.fail.Exception): with assert_num_queries(3): Item.objects.create(name='foo') Item.objects.create(name='bar') From caa952cf2edbc856e746c05f0cfd27648a250a7f Mon Sep 17 00:00:00 2001 From: Lukasz Balcerzak Date: Wed, 31 Aug 2016 09:23:40 +0200 Subject: [PATCH 05/11] test against transactional_db --- pytest_django/fixtures.py | 2 +- tests/test_fixtures.py | 20 ++++++++++++++------ 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/pytest_django/fixtures.py b/pytest_django/fixtures.py index 3b6a32b28..2681686e0 100644 --- a/pytest_django/fixtures.py +++ b/pytest_django/fixtures.py @@ -328,7 +328,7 @@ def _live_server_helper(request): @pytest.fixture(scope='function') -def assert_num_queries(db, pytestconfig): +def assert_num_queries(pytestconfig): from django.db import connection from django.test.utils import CaptureQueriesContext diff --git a/tests/test_fixtures.py b/tests/test_fixtures.py index 0754b346c..d48ce83cf 100644 --- a/tests/test_fixtures.py +++ b/tests/test_fixtures.py @@ -50,19 +50,27 @@ def test_rf(rf): @pytest.mark.django_db -def test_assert_num_queries(assert_num_queries, django_user_model): +def test_assert_num_queries_db(assert_num_queries, django_user_model): 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(assert_num_queries, django_user_model): + with assert_num_queries(3): + Item.objects.create(name='foo') + Item.objects.create(name='bar') + Item.objects.create(name='baz') -@pytest.mark.django_db -def test_assert_num_queries_fails_properly(assert_num_queries, django_user_model): with pytest.raises(pytest.fail.Exception): - with assert_num_queries(3): - Item.objects.create(name='foo') - Item.objects.create(name='bar') + with assert_num_queries(2): + Item.objects.create(name='quux') class TestSettings: From cf8e6fd7e765c5b6971ebf3ad6163e27be902de3 Mon Sep 17 00:00:00 2001 From: Lukasz Balcerzak Date: Wed, 31 Aug 2016 09:26:17 +0200 Subject: [PATCH 06/11] removed unused fixture from tests --- tests/test_fixtures.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_fixtures.py b/tests/test_fixtures.py index d48ce83cf..fabf5467f 100644 --- a/tests/test_fixtures.py +++ b/tests/test_fixtures.py @@ -50,7 +50,7 @@ def test_rf(rf): @pytest.mark.django_db -def test_assert_num_queries_db(assert_num_queries, django_user_model): +def test_assert_num_queries_db(assert_num_queries): with assert_num_queries(3): Item.objects.create(name='foo') Item.objects.create(name='bar') @@ -62,7 +62,7 @@ def test_assert_num_queries_db(assert_num_queries, django_user_model): @pytest.mark.django_db(transaction=True) -def test_assert_num_queries_transactional_db(assert_num_queries, django_user_model): +def test_assert_num_queries_transactional_db(assert_num_queries): with assert_num_queries(3): Item.objects.create(name='foo') Item.objects.create(name='bar') From 61b8d840189831296f716b801c51b24adf1752ce Mon Sep 17 00:00:00 2001 From: Lukasz Balcerzak Date: Wed, 31 Aug 2016 09:46:46 +0200 Subject: [PATCH 07/11] test output --- tests/test_fixtures.py | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/tests/test_fixtures.py b/tests/test_fixtures.py index fabf5467f..47a0a6db7 100644 --- a/tests/test_fixtures.py +++ b/tests/test_fixtures.py @@ -73,6 +73,42 @@ def test_assert_num_queries_transactional_db(assert_num_queries): 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""" From ab0bbca8d301173871c0b66c435cbc9715d2f006 Mon Sep 17 00:00:00 2001 From: Lukasz Balcerzak Date: Wed, 31 Aug 2016 10:59:53 +0200 Subject: [PATCH 08/11] fix transactional test --- tests/test_fixtures.py | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/tests/test_fixtures.py b/tests/test_fixtures.py index 47a0a6db7..0ae76dba7 100644 --- a/tests/test_fixtures.py +++ b/tests/test_fixtures.py @@ -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 @@ -62,15 +62,17 @@ def test_assert_num_queries_db(assert_num_queries): @pytest.mark.django_db(transaction=True) -def test_assert_num_queries_transactional_db(assert_num_queries): - with assert_num_queries(3): - Item.objects.create(name='foo') - Item.objects.create(name='bar') - Item.objects.create(name='baz') +def test_assert_num_queries_transactional_db(transactional_db, assert_num_queries): + with transaction.atomic(): - with pytest.raises(pytest.fail.Exception): - with assert_num_queries(2): - Item.objects.create(name='quux') + 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') def test_assert_num_queries_output(django_testdir): From 03dd0a7b3e1c0a632edfb8161e699af81f7b854c Mon Sep 17 00:00:00 2001 From: Lukasz Balcerzak Date: Mon, 26 Sep 2016 19:19:51 +0200 Subject: [PATCH 09/11] post review fixes --- docs/helpers.rst | 4 ++-- pytest_django/fixtures.py | 4 ++-- pytest_django/plugin.py | 2 +- tests/test_fixtures.py | 24 ++++++++++++------------ 4 files changed, 17 insertions(+), 17 deletions(-) diff --git a/docs/helpers.rst b/docs/helpers.rst index a1eb3a17e..3985f6ea8 100644 --- a/docs/helpers.rst +++ b/docs/helpers.rst @@ -221,8 +221,8 @@ Example ``assert_num_queries`` ~~~~~~~~~~~~~~~~~~~~~~ -This fixture allows to check that expected number of queries are performed. -Note that currently it only supports default database. +This fixture allows to check that expected number DB of queries are performed. +Note that it currently only supports the default database. Example diff --git a/pytest_django/fixtures.py b/pytest_django/fixtures.py index 2681686e0..cbd132392 100644 --- a/pytest_django/fixtures.py +++ b/pytest_django/fixtures.py @@ -17,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', 'assert_num_queries'] + '_live_server_helper', 'django_assert_num_queries'] @pytest.fixture(scope='session') @@ -328,7 +328,7 @@ def _live_server_helper(request): @pytest.fixture(scope='function') -def assert_num_queries(pytestconfig): +def django_assert_num_queries(pytestconfig): from django.db import connection from django.test.utils import CaptureQueriesContext diff --git a/pytest_django/plugin.py b/pytest_django/plugin.py index 1e2a3c505..5051e4e7a 100644 --- a/pytest_django/plugin.py +++ b/pytest_django/plugin.py @@ -15,6 +15,7 @@ import pytest from .django_compat import is_django_unittest # noqa +from .fixtures import django_assert_num_queries # noqa from .fixtures import django_db_setup # noqa from .fixtures import django_db_use_migrations # noqa from .fixtures import django_db_keepdb # noqa @@ -23,7 +24,6 @@ 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 diff --git a/tests/test_fixtures.py b/tests/test_fixtures.py index 0ae76dba7..331f92a29 100644 --- a/tests/test_fixtures.py +++ b/tests/test_fixtures.py @@ -50,39 +50,39 @@ def test_rf(rf): @pytest.mark.django_db -def test_assert_num_queries_db(assert_num_queries): - with assert_num_queries(3): +def test_django_assert_num_queries_db(django_assert_num_queries): + with django_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): + with django_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): +def test_django_assert_num_queries_transactional_db(transactional_db, django_assert_num_queries): with transaction.atomic(): - with assert_num_queries(3): + with django_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): + with django_assert_num_queries(2): Item.objects.create(name='quux') -def test_assert_num_queries_output(django_testdir): +def test_django_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): + def test_queries(django_assert_num_queries): + with django_assert_num_queries(1): list(ContentType.objects.all()) ContentType.objects.count() """) @@ -91,14 +91,14 @@ def test_queries(assert_num_queries): assert result.ret == 1 -def test_assert_num_queries_output_verbose(django_testdir): +def test_django_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): + def test_queries(django_assert_num_queries): + with django_assert_num_queries(11): list(ContentType.objects.all()) ContentType.objects.count() """) From d41f2ddb5939f9d5aaab98d89e03e6c27a6cb28f Mon Sep 17 00:00:00 2001 From: Lukasz Balcerzak Date: Mon, 7 Nov 2016 08:27:28 +0100 Subject: [PATCH 10/11] review fixes --- docs/helpers.rst | 4 ++-- pytest_django/fixtures.py | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/helpers.rst b/docs/helpers.rst index a1eb3a17e..794874221 100644 --- a/docs/helpers.rst +++ b/docs/helpers.rst @@ -221,8 +221,8 @@ Example ``assert_num_queries`` ~~~~~~~~~~~~~~~~~~~~~~ -This fixture allows to check that expected number of queries are performed. -Note that currently it only supports default database. +This fixture allows to check for an expected number of DB queries. +It currently only supports the default database. Example diff --git a/pytest_django/fixtures.py b/pytest_django/fixtures.py index 2681686e0..ec4e5a6e5 100644 --- a/pytest_django/fixtures.py +++ b/pytest_django/fixtures.py @@ -336,13 +336,13 @@ def assert_num_queries(pytestconfig): 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): + 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)" pytest.fail(msg) return _assert_num_queries From 5d36c0b0b6ff6a8d6877873d2938f66337cd0acc Mon Sep 17 00:00:00 2001 From: Lukasz Balcerzak Date: Tue, 7 Feb 2017 21:07:05 +0100 Subject: [PATCH 11/11] Updated documentation --- docs/helpers.rst | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/docs/helpers.rst b/docs/helpers.rst index dc18fe822..ee5c424ec 100644 --- a/docs/helpers.rst +++ b/docs/helpers.rst @@ -218,8 +218,9 @@ Example settings.USE_TZ = True assert settings.USE_TZ -``assert_num_queries`` -~~~~~~~~~~~~~~~~~~~~~~ + +``django_assert_num_queries`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This fixture allows to check for an expected number of DB queries. It currently only supports the default database. @@ -231,11 +232,12 @@ Example :: def test_queries(assert_num_queries): - with assert_num_queries(3): + with django_assert_num_queries(3): Item.objects.create('foo') Item.objects.create('bar') Item.objects.create('baz') + ``mailoutbox`` ~~~~~~~~~~~~~~~~~~~~~~~~~