Author: carljm
Date: 2012-01-31 11:23:09 -0800 (Tue, 31 Jan 2012)
New Revision: 17412

Added:
   django/trunk/tests/regressiontests/test_utils/templates/
   django/trunk/tests/regressiontests/test_utils/templates/template_used/
   
django/trunk/tests/regressiontests/test_utils/templates/template_used/alternative.html
   
django/trunk/tests/regressiontests/test_utils/templates/template_used/base.html
   
django/trunk/tests/regressiontests/test_utils/templates/template_used/extends.html
   
django/trunk/tests/regressiontests/test_utils/templates/template_used/include.html
Modified:
   django/trunk/django/test/testcases.py
   django/trunk/docs/releases/1.4.txt
   django/trunk/docs/topics/testing.txt
   django/trunk/tests/regressiontests/test_utils/tests.py
Log:
Fixed #17604 - Added context-manager capability to assertTemplateUsed and 
assertTemplateNotUsed. Thanks Greg M?\195?\188llegger.

Modified: django/trunk/django/test/testcases.py
===================================================================
--- django/trunk/django/test/testcases.py       2012-01-30 08:27:50 UTC (rev 
17411)
+++ django/trunk/django/test/testcases.py       2012-01-31 19:23:09 UTC (rev 
17412)
@@ -3,6 +3,7 @@
 import os
 import re
 import sys
+from copy import copy
 from functools import wraps
 from urlparse import urlsplit, urlunsplit
 from xml.dom.minidom import parseString, Node
@@ -28,8 +29,10 @@
 from django.http import QueryDict
 from django.test import _doctest as doctest
 from django.test.client import Client
+from django.test.signals import template_rendered
 from django.test.utils import (get_warnings_state, restore_warnings_state,
     override_settings)
+from django.test.utils import ContextList
 from django.utils import simplejson, unittest as ut2
 from django.utils.encoding import smart_str, force_unicode
 from django.views.static import serve
@@ -260,8 +263,53 @@
         )
 
 
+class _AssertTemplateUsedContext(object):
+    def __init__(self, test_case, template_name):
+        self.test_case = test_case
+        self.template_name = template_name
+        self.rendered_templates = []
+        self.rendered_template_names = []
+        self.context = ContextList()
+
+    def on_template_render(self, sender, signal, template, context, **kwargs):
+        self.rendered_templates.append(template)
+        self.rendered_template_names.append(template.name)
+        self.context.append(copy(context))
+
+    def test(self):
+        return self.template_name in self.rendered_template_names
+
+    def message(self):
+        return u'%s was not rendered.' % self.template_name
+
+    def __enter__(self):
+        template_rendered.connect(self.on_template_render)
+        return self
+
+    def __exit__(self, exc_type, exc_value, traceback):
+        template_rendered.disconnect(self.on_template_render)
+        if exc_type is not None:
+            return
+
+        if not self.test():
+            message = self.message()
+            if len(self.rendered_templates) == 0:
+                message += u' No template was rendered.'
+            else:
+                message += u' Following templates were rendered: %s' % (
+                    ', '.join(self.rendered_template_names))
+            self.test_case.fail(message)
+
+
+class _AssertTemplateNotUsedContext(_AssertTemplateUsedContext):
+    def test(self):
+        return self.template_name not in self.rendered_template_names
+
+    def message(self):
+        return u'%s was rendered.' % self.template_name
+
+
 class SimpleTestCase(ut2.TestCase):
-
     def save_warnings_state(self):
         """
         Saves the state of the warnings module
@@ -612,14 +660,25 @@
             self.fail(msg_prefix + "The form '%s' was not used to render the"
                       " response" % form)
 
-    def assertTemplateUsed(self, response, template_name, msg_prefix=''):
+    def assertTemplateUsed(self, response=None, template_name=None, 
msg_prefix=''):
         """
         Asserts that the template with the provided name was used in rendering
-        the response.
+        the response. Also useable as context manager.
         """
+        if response is None and template_name is None:
+            raise TypeError(u'response and/or template_name argument must be 
provided')
+
         if msg_prefix:
             msg_prefix += ": "
 
+        # use assertTemplateUsed as context manager
+        if not hasattr(response, 'templates') or (response is None and 
template_name):
+            if response:
+                template_name = response
+                response = None
+            context = _AssertTemplateUsedContext(self, template_name)
+            return context
+
         template_names = [t.name for t in response.templates]
         if not template_names:
             self.fail(msg_prefix + "No templates used to render the response")
@@ -628,14 +687,25 @@
             " the response. Actual template(s) used: %s" %
                 (template_name, u', '.join(template_names)))
 
-    def assertTemplateNotUsed(self, response, template_name, msg_prefix=''):
+    def assertTemplateNotUsed(self, response=None, template_name=None, 
msg_prefix=''):
         """
         Asserts that the template with the provided name was NOT used in
-        rendering the response.
+        rendering the response. Also useable as context manager.
         """
+        if response is None and template_name is None:
+            raise TypeError(u'response and/or template_name argument must be 
provided')
+
         if msg_prefix:
             msg_prefix += ": "
 
+        # use assertTemplateUsed as context manager
+        if not hasattr(response, 'templates') or (response is None and 
template_name):
+            if response:
+                template_name = response
+                response = None
+            context = _AssertTemplateNotUsedContext(self, template_name)
+            return context
+
         template_names = [t.name for t in response.templates]
         self.assertFalse(template_name in template_names,
             msg_prefix + "Template '%s' was used unexpectedly in rendering"

Modified: django/trunk/docs/releases/1.4.txt
===================================================================
--- django/trunk/docs/releases/1.4.txt  2012-01-30 08:27:50 UTC (rev 17411)
+++ django/trunk/docs/releases/1.4.txt  2012-01-31 19:23:09 UTC (rev 17412)
@@ -946,6 +946,21 @@
 a ``%xx`` sequence, but such URLs are very unlikely to happen in the wild,
 since they would confuse browsers too.
 
+``assertTemplateUsed`` and ``assertTemplateNotUsed`` as context manager
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+It is now possible to check whether a template was used or not in a block of
+code with the :meth:`~django.test.testcase.TestCase.assertTemplateUsed` and
+:meth:`~django.test.testcase.TestCase.assertTemplateNotUsed` assertions. They
+can be used as a context manager::
+
+    with self.assertTemplateUsed('index.html'):
+        render_to_string('index.html')
+    with self.assertTemplateNotUsed('base.html'):
+        render_to_string('index.html')
+
+See the :ref:`assertion documentation<assertions>` for more information.
+
 Database connections after running the test suite
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 

Modified: django/trunk/docs/topics/testing.txt
===================================================================
--- django/trunk/docs/topics/testing.txt        2012-01-30 08:27:50 UTC (rev 
17411)
+++ django/trunk/docs/topics/testing.txt        2012-01-31 19:23:09 UTC (rev 
17412)
@@ -1575,11 +1575,30 @@
 
     The name is a string such as ``'admin/index.html'``.
 
+    .. versionadded:: 1.4
+
+    You can also use this as a context manager. The code that is executed
+    under the with statement is then observed instead of a response::
+
+        # This is necessary in Python 2.5 to enable the with statement, in 2.6
+        # and up it is no longer necessary.
+        from __future__ import with_statement
+
+        with self.assertTemplateUsed('index.html'):
+            render_to_string('index.html')
+        with self.assertTemplateUsed(template_name='index.html'):
+            render_to_string('index.html')
+
 .. method:: TestCase.assertTemplateNotUsed(response, template_name, 
msg_prefix='')
 
     Asserts that the template with the given name was *not* used in rendering
     the response.
 
+    .. versionadded:: 1.4
+
+    You can use this as a context manager in the same way as
+    :func:`~TestCase.assertTemplateUsed`.
+
 .. method:: TestCase.assertRedirects(response, expected_url, status_code=302, 
target_status_code=200, msg_prefix='')
 
     Asserts that the response return a ``status_code`` redirect status, it

Added: 
django/trunk/tests/regressiontests/test_utils/templates/template_used/alternative.html
===================================================================
Added: 
django/trunk/tests/regressiontests/test_utils/templates/template_used/base.html
===================================================================
Added: 
django/trunk/tests/regressiontests/test_utils/templates/template_used/extends.html
===================================================================
--- 
django/trunk/tests/regressiontests/test_utils/templates/template_used/extends.html
                          (rev 0)
+++ 
django/trunk/tests/regressiontests/test_utils/templates/template_used/extends.html
  2012-01-31 19:23:09 UTC (rev 17412)
@@ -0,0 +1 @@
+{% extends "template_used/base.html" %}

Added: 
django/trunk/tests/regressiontests/test_utils/templates/template_used/include.html
===================================================================
--- 
django/trunk/tests/regressiontests/test_utils/templates/template_used/include.html
                          (rev 0)
+++ 
django/trunk/tests/regressiontests/test_utils/templates/template_used/include.html
  2012-01-31 19:23:09 UTC (rev 17412)
@@ -0,0 +1 @@
+{% include "template_used/base.html" %}

Modified: django/trunk/tests/regressiontests/test_utils/tests.py
===================================================================
--- django/trunk/tests/regressiontests/test_utils/tests.py      2012-01-30 
08:27:50 UTC (rev 17411)
+++ django/trunk/tests/regressiontests/test_utils/tests.py      2012-01-31 
19:23:09 UTC (rev 17412)
@@ -1,6 +1,7 @@
 from __future__ import with_statement, absolute_import
 
 from django.forms import EmailField, IntegerField
+from django.template.loader import render_to_string
 from django.test import SimpleTestCase, TestCase, skipUnlessDBFeature
 from django.utils.unittest import skip
 
@@ -88,6 +89,92 @@
             self.client.get("/test_utils/get_person/%s/" % person.pk)
 
 
+class AssertTemplateUsedContextManagerTests(TestCase):
+    def test_usage(self):
+        with self.assertTemplateUsed('template_used/base.html'):
+            render_to_string('template_used/base.html')
+
+        with self.assertTemplateUsed(template_name='template_used/base.html'):
+            render_to_string('template_used/base.html')
+
+        with self.assertTemplateUsed('template_used/base.html'):
+            render_to_string('template_used/include.html')
+
+        with self.assertTemplateUsed('template_used/base.html'):
+            render_to_string('template_used/extends.html')
+
+        with self.assertTemplateUsed('template_used/base.html'):
+            render_to_string('template_used/base.html')
+            render_to_string('template_used/base.html')
+
+    def test_nested_usage(self):
+        with self.assertTemplateUsed('template_used/base.html'):
+            with self.assertTemplateUsed('template_used/include.html'):
+                render_to_string('template_used/include.html')
+
+        with self.assertTemplateUsed('template_used/extends.html'):
+            with self.assertTemplateUsed('template_used/base.html'):
+                render_to_string('template_used/extends.html')
+
+        with self.assertTemplateUsed('template_used/base.html'):
+            with self.assertTemplateUsed('template_used/alternative.html'):
+                render_to_string('template_used/alternative.html')
+            render_to_string('template_used/base.html')
+
+        with self.assertTemplateUsed('template_used/base.html'):
+            render_to_string('template_used/extends.html')
+            with self.assertTemplateNotUsed('template_used/base.html'):
+                render_to_string('template_used/alternative.html')
+            render_to_string('template_used/base.html')
+
+    def test_not_used(self):
+        with self.assertTemplateNotUsed('template_used/base.html'):
+            pass
+        with self.assertTemplateNotUsed('template_used/alternative.html'):
+            pass
+
+    def test_error_message(self):
+        try:
+            with self.assertTemplateUsed('template_used/base.html'):
+                pass
+        except AssertionError, e:
+            self.assertTrue('template_used/base.html' in e.message)
+
+        try:
+            with 
self.assertTemplateUsed(template_name='template_used/base.html'):
+                pass
+        except AssertionError, e:
+            self.assertTrue('template_used/base.html' in e.message)
+
+        try:
+            with self.assertTemplateUsed('template_used/base.html'):
+                render_to_string('template_used/alternative.html')
+        except AssertionError, e:
+            self.assertTrue('template_used/base.html' in e.message, e.message)
+            self.assertTrue('template_used/alternative.html' in e.message, 
e.message)
+
+    def test_failure(self):
+        with self.assertRaises(TypeError):
+            with self.assertTemplateUsed():
+                pass
+
+        with self.assertRaises(AssertionError):
+            with self.assertTemplateUsed(''):
+                pass
+
+        with self.assertRaises(AssertionError):
+            with self.assertTemplateUsed(''):
+                render_to_string('template_used/base.html')
+
+        with self.assertRaises(AssertionError):
+            with self.assertTemplateUsed(template_name=''):
+                pass
+
+        with self.assertRaises(AssertionError):
+            with self.assertTemplateUsed('template_used/base.html'):
+                render_to_string('template_used/alternative.html')
+
+
 class SaveRestoreWarningState(TestCase):
     def test_save_restore_warnings_state(self):
         """

-- 
You received this message because you are subscribed to the Google Groups 
"Django updates" group.
To post to this group, send email to [email protected].
To unsubscribe from this group, send email to 
[email protected].
For more options, visit this group at 
http://groups.google.com/group/django-updates?hl=en.

Reply via email to