Code

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 charettes, 17 months 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