Ticket #10399: 0001-Added-a-context-manager-to-capture-queries-while-tes.patch

File 0001-Added-a-context-manager-to-capture-queries-while-tes.patch, 12.0 KB (added by Simon Charette, 11 years ago)

Added CaptureQueriesContext with tests

  • django/test/testcases.py

    From 9ddc8e7d48cc5fa2e838e47b4d22b51a6bf294ea Mon Sep 17 00:00:00 2001
    From: Simon Charette <charette.s@gmail.com>
    Date: Fri, 1 Mar 2013 15:29:39 -0500
    Subject: [PATCH 1/2] Added a context manager to capture queries while
     testing.
    
    Also made some import cleanups while I was there.
    ---
     django/test/testcases.py  |   35 ++++++++---------------
     django/test/utils.py      |   39 ++++++++++++++++++++++++++
     tests/test_utils/tests.py |   67 +++++++++++++++++++++++++++++++++++++++------
     3 files changed, 108 insertions(+), 33 deletions(-)
    
    diff --git a/django/test/testcases.py b/django/test/testcases.py
    index f9d028b..345e4b1 100644
    a b from django.core.exceptions import ValidationError, ImproperlyConfigured  
    2424from django.core.handlers.wsgi import WSGIHandler
    2525from django.core.management import call_command
    2626from django.core.management.color import no_style
    27 from django.core.signals import request_started
    2827from django.core.servers.basehttp import (WSGIRequestHandler, WSGIServer,
    2928    WSGIServerException)
    3029from django.core.urlresolvers import clear_url_caches
    3130from django.core.validators import EMPTY_VALUES
    32 from django.db import (transaction, connection, connections, DEFAULT_DB_ALIAS,
    33     reset_queries)
     31from django.db import connection, connections, DEFAULT_DB_ALIAS, transaction
    3432from django.forms.fields import CharField
    3533from django.http import QueryDict
    3634from django.test import _doctest as doctest
    3735from django.test.client import Client
    3836from django.test.html import HTMLParseError, parse_html
    3937from django.test.signals import template_rendered
    40 from django.test.utils import (override_settings, compare_xml, strip_quotes)
    41 from django.test.utils import ContextList
    42 from django.utils import unittest as ut2
     38from django.test.utils import (CaptureQueriesContext, ContextList,
     39    override_settings, compare_xml, strip_quotes)
     40from django.utils import six, unittest as ut2
    4341from django.utils.encoding import force_text
    44 from django.utils import six
     42from django.utils.unittest import skipIf # Imported here for backward compatibility
    4543from django.utils.unittest.util import safe_repr
    46 from django.utils.unittest import skipIf
    4744from django.views.static import serve
    4845
     46
    4947__all__ = ('DocTestRunner', 'OutputChecker', 'TestCase', 'TransactionTestCase',
    5048           'SimpleTestCase', 'skipIfDBFeature', 'skipUnlessDBFeature')
    5149
     50
    5251normalize_long_ints = lambda s: re.sub(r'(?<![\w])(\d+)L(?![\w])', '\\1', s)
    5352normalize_decimals = lambda s: re.sub(r"Decimal\('(\d+(\.\d*)?)'\)",
    5453                                lambda m: "Decimal(\"%s\")" % m.groups()[0], s)
    class DocTestRunner(doctest.DocTestRunner):  
    168167            transaction.rollback_unless_managed(using=conn)
    169168
    170169
    171 class _AssertNumQueriesContext(object):
     170class _AssertNumQueriesContext(CaptureQueriesContext):
    172171    def __init__(self, test_case, num, connection):
    173172        self.test_case = test_case
    174173        self.num = num
    175         self.connection = connection
    176 
    177     def __enter__(self):
    178         self.old_debug_cursor = self.connection.use_debug_cursor
    179         self.connection.use_debug_cursor = True
    180         self.starting_queries = len(self.connection.queries)
    181         request_started.disconnect(reset_queries)
    182         return self
     174        super(_AssertNumQueriesContext, self).__init__(connection)
    183175
    184176    def __exit__(self, exc_type, exc_value, traceback):
    185         self.connection.use_debug_cursor = self.old_debug_cursor
    186         request_started.connect(reset_queries)
    187177        if exc_type is not None:
    188178            return
    189 
    190         final_queries = len(self.connection.queries)
    191         executed = final_queries - self.starting_queries
    192 
     179        super(_AssertNumQueriesContext, self).__exit__(exc_type, exc_value, traceback)
     180        executed = len(self)
    193181        self.test_case.assertEqual(
    194182            executed, self.num, "%d queries executed, %d expected" % (
    195183                executed, self.num
    class LiveServerThread(threading.Thread):  
    10511039        http requests.
    10521040        """
    10531041        if self.connections_override:
    1054             from django.db import connections
    10551042            # Override this thread's database connections with the ones
    10561043            # provided by the main thread.
    10571044            for alias, conn in self.connections_override.items():
  • django/test/utils.py

    diff --git a/django/test/utils.py b/django/test/utils.py
    index 5d20120f..d839c04 100644
    a b from xml.dom.minidom import parseString, Node  
    44
    55from django.conf import settings, UserSettingsHolder
    66from django.core import mail
     7from django.core.signals import request_started
     8from django.db import reset_queries
    79from django.template import Template, loader, TemplateDoesNotExist
    810from django.template.loaders import cached
    911from django.test.signals import template_rendered, setting_changed
    def strip_quotes(want, got):  
    339341        got = got.strip()[2:-1]
    340342    return want, got
    341343
     344
    342345def str_prefix(s):
    343346    return s % {'_': '' if six.PY3 else 'u'}
     347
     348
     349class CaptureQueriesContext(object):
     350    """
     351    Context manager that captures queries executed by the specified connection.
     352    """
     353    def __init__(self, connection):
     354        self.connection = connection
     355
     356    def __iter__(self):
     357        return iter(self.captured_queries)
     358
     359    def __getitem__(self, index):
     360        return self.captured_queries[index]
     361
     362    def __len__(self):
     363        return len(self.captured_queries)
     364
     365    @property
     366    def captured_queries(self):
     367        return self.connection.queries[self.initial_queries:self.final_queries]
     368
     369    def __enter__(self):
     370        self.use_debug_cursor = self.connection.use_debug_cursor
     371        self.connection.use_debug_cursor = True
     372        self.initial_queries = len(self.connection.queries)
     373        self.final_queries = None
     374        request_started.disconnect(reset_queries)
     375        return self
     376
     377    def __exit__(self, exc_type, exc_value, traceback):
     378        self.connection.use_debug_cursor = self.use_debug_cursor
     379        request_started.connect(reset_queries)
     380        if exc_type is not None:
     381            return
     382        self.final_queries = len(self.connection.queries)
  • tests/test_utils/tests.py

    diff --git a/tests/test_utils/tests.py b/tests/test_utils/tests.py
    index 7573849..f0cfac7 100644
    a b  
    11# -*- coding: utf-8 -*-
    22from __future__ import absolute_import, unicode_literals
     3import warnings
    34
     5from django.db import connection
    46from django.forms import EmailField, IntegerField
    57from django.http import HttpResponse
    68from django.template.loader import render_to_string
    79from django.test import SimpleTestCase, TestCase, skipUnlessDBFeature
     10from django.test.html import HTMLParseError, parse_html
     11from django.test.utils import CaptureQueriesContext
    812from django.utils import six
    913from django.utils.unittest import skip
    1014
    class AssertQuerysetEqualTests(TestCase):  
    9498        )
    9599
    96100
     101class CaptureQueriesContextManagerTests(TestCase):
     102    urls = 'test_utils.urls'
     103
     104    def setUp(self):
     105        self.person_pk = six.text_type(Person.objects.create(name='test').pk)
     106
     107    def test_simple(self):
     108        with CaptureQueriesContext(connection) as captured_queries:
     109            Person.objects.get(pk=self.person_pk)
     110        self.assertEqual(len(captured_queries), 1)
     111        self.assertIn(self.person_pk, captured_queries[0]['sql'])
     112
     113        with CaptureQueriesContext(connection) as captured_queries:
     114            pass
     115        self.assertEqual(0, len(captured_queries))
     116
     117    def test_within(self):
     118        with CaptureQueriesContext(connection) as captured_queries:
     119            Person.objects.get(pk=self.person_pk)
     120            self.assertEqual(len(captured_queries), 1)
     121            self.assertIn(self.person_pk, captured_queries[0]['sql'])
     122
     123    def test_nested(self):
     124        with CaptureQueriesContext(connection) as captured_queries:
     125            Person.objects.count()
     126            with CaptureQueriesContext(connection) as nested_captured_queries:
     127                Person.objects.count()
     128        self.assertEqual(1, len(nested_captured_queries))
     129        self.assertEqual(2, len(captured_queries))
     130
     131    def test_failure(self):
     132        with self.assertRaises(TypeError):
     133            with CaptureQueriesContext(connection):
     134                raise TypeError
     135
     136    def test_with_client(self):
     137        with CaptureQueriesContext(connection) as captured_queries:
     138            self.client.get("/test_utils/get_person/%s/" % self.person_pk)
     139        self.assertEqual(len(captured_queries), 1)
     140        self.assertIn(self.person_pk, captured_queries[0]['sql'])
     141
     142        with CaptureQueriesContext(connection) as captured_queries:
     143            self.client.get("/test_utils/get_person/%s/" % self.person_pk)
     144        self.assertEqual(len(captured_queries), 1)
     145        self.assertIn(self.person_pk, captured_queries[0]['sql'])
     146
     147        with CaptureQueriesContext(connection) as captured_queries:
     148            self.client.get("/test_utils/get_person/%s/" % self.person_pk)
     149            self.client.get("/test_utils/get_person/%s/" % self.person_pk)
     150        self.assertEqual(len(captured_queries), 2)
     151        self.assertIn(self.person_pk, captured_queries[0]['sql'])
     152        self.assertIn(self.person_pk, captured_queries[1]['sql'])
     153
     154
    97155class AssertNumQueriesContextManagerTests(TestCase):
    98156    urls = 'test_utils.urls'
    99157
    class SaveRestoreWarningState(TestCase):  
    219277        # In reality this test could be satisfied by many broken implementations
    220278        # of save_warnings_state/restore_warnings_state (e.g. just
    221279        # warnings.resetwarnings()) , but it is difficult to test more.
    222         import warnings
    223280        with warnings.catch_warnings():
    224281            warnings.simplefilter("ignore", DeprecationWarning)
    225282
    class SaveRestoreWarningState(TestCase):  
    245302
    246303class HTMLEqualTests(TestCase):
    247304    def test_html_parser(self):
    248         from django.test.html import parse_html
    249305        element = parse_html('<div><p>Hello</p></div>')
    250306        self.assertEqual(len(element.children), 1)
    251307        self.assertEqual(element.children[0].name, 'p')
    class HTMLEqualTests(TestCase):  
    259315        self.assertEqual(dom[0], 'foo')
    260316
    261317    def test_parse_html_in_script(self):
    262         from django.test.html import parse_html
    263318        parse_html('<script>var a = "<p" + ">";</script>');
    264319        parse_html('''
    265320            <script>
    class HTMLEqualTests(TestCase):  
    275330        self.assertEqual(dom.children[0], "<p>foo</p> '</scr'+'ipt>' <span>bar</span>")
    276331
    277332    def test_self_closing_tags(self):
    278         from django.test.html import parse_html
    279 
    280333        self_closing_tags = ('br' , 'hr', 'input', 'img', 'meta', 'spacer',
    281334            'link', 'frame', 'base', 'col')
    282335        for tag in self_closing_tags:
    class HTMLEqualTests(TestCase):  
    400453        </html>""")
    401454
    402455    def test_html_contain(self):
    403         from django.test.html import parse_html
    404456        # equal html contains each other
    405457        dom1 = parse_html('<p>foo')
    406458        dom2 = parse_html('<p>foo</p>')
    class HTMLEqualTests(TestCase):  
    424476        self.assertTrue(dom1 in dom2)
    425477
    426478    def test_count(self):
    427         from django.test.html import parse_html
    428479        # equal html contains each other one time
    429480        dom1 = parse_html('<p>foo')
    430481        dom2 = parse_html('<p>foo</p>')
    class HTMLEqualTests(TestCase):  
    459510        self.assertEqual(dom2.count(dom1), 0)
    460511
    461512    def test_parsing_errors(self):
    462         from django.test.html import HTMLParseError, parse_html
    463513        with self.assertRaises(AssertionError):
    464514            self.assertHTMLEqual('<p>', '')
    465515        with self.assertRaises(AssertionError):
    class HTMLEqualTests(TestCase):  
    488538            self.assertContains(response, '<p "whats" that>')
    489539
    490540    def test_unicode_handling(self):
    491         from django.http import HttpResponse
    492541        response = HttpResponse('<p class="help">Some help text for the title (with unicode ŠĐĆŽćžšđ)</p>')
    493542        self.assertContains(response, '<p class="help">Some help text for the title (with unicode ŠĐĆŽćžšđ)</p>', html=True)
    494543
Back to Top