Fixed #12226 -- Deprecated test client Response.template attribute in favor of templates attribute, which is always a list. Thanks Russell for patch review.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@14106 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Carl Meyer 2010-10-10 02:16:33 +00:00
parent d084439c41
commit 501546df6f
7 changed files with 76 additions and 26 deletions

View File

@ -4,6 +4,7 @@ import sys
import os import os
import re import re
import mimetypes import mimetypes
import warnings
try: try:
from cStringIO import StringIO from cStringIO import StringIO
except ImportError: except ImportError:
@ -93,7 +94,7 @@ def store_rendered_templates(store, signal, sender, template, context, **kwargs)
""" """
Stores templates and contexts that are rendered. Stores templates and contexts that are rendered.
""" """
store.setdefault('template', []).append(template) store.setdefault('templates', []).append(template)
store.setdefault('context', ContextList()).append(context) store.setdefault('context', ContextList()).append(context)
def encode_multipart(boundary, data): def encode_multipart(boundary, data):
@ -260,16 +261,25 @@ class Client(object):
response.request = request response.request = request
# Add any rendered template detail to the response. # Add any rendered template detail to the response.
# If there was only one template rendered (the most likely case), response.templates = data.get("templates", [])
# flatten the list to a single element. response.context = data.get("context")
for detail in ('template', 'context'):
if data.get(detail): # Flatten a single context. Not really necessary anymore thanks to
if len(data[detail]) == 1: # the __getattr__ flattening in ContextList, but has some edge-case
setattr(response, detail, data[detail][0]); # backwards-compatibility implications.
else: if response.context and len(response.context) == 1:
setattr(response, detail, data[detail]) response.context = response.context[0]
else:
setattr(response, detail, None) # Provide a backwards-compatible (but pending deprecation) response.template
def _get_template(self):
warnings.warn("response.template is deprecated; use response.templates instead (which is always a list)",
PendingDeprecationWarning)
if not self.templates:
return None
elif len(self.templates) == 1:
return self.templates[0]
return self.templates
response.__class__.template = property(_get_template)
# Update persistent cookie data. # Update persistent cookie data.
if response.cookies: if response.cookies:

View File

@ -443,7 +443,7 @@ class TransactionTestCase(unittest.TestCase):
if msg_prefix: if msg_prefix:
msg_prefix += ": " msg_prefix += ": "
template_names = [t.name for t in to_list(response.template)] template_names = [t.name for t in response.templates]
if not template_names: if not template_names:
self.fail(msg_prefix + "No templates used to render the response") self.fail(msg_prefix + "No templates used to render the response")
self.failUnless(template_name in template_names, self.failUnless(template_name in template_names,
@ -459,7 +459,7 @@ class TransactionTestCase(unittest.TestCase):
if msg_prefix: if msg_prefix:
msg_prefix += ": " msg_prefix += ": "
template_names = [t.name for t in to_list(response.template)] template_names = [t.name for t in response.templates]
self.failIf(template_name in template_names, self.failIf(template_name in template_names,
msg_prefix + "Template '%s' was used unexpectedly in rendering" msg_prefix + "Template '%s' was used unexpectedly in rendering"
" the response" % template_name) " the response" % template_name)

View File

@ -102,6 +102,12 @@ their deprecation, as per the :ref:`Django deprecation policy
* The ``mod_python`` request handler has been deprecated since the 1.3 * The ``mod_python`` request handler has been deprecated since the 1.3
release. The ``mod_wsgi`` handler should be used instead. release. The ``mod_wsgi`` handler should be used instead.
* The ``template`` attribute on :class:`~django.test.client.Response`
objects returned by the :ref:`test client <test-client>` has been
deprecated since the 1.3 release. The
:attr:`~django.test.client.Response.templates` attribute should be
used instead.
* 2.0 * 2.0
* ``django.views.defaults.shortcut()``. This function has been moved * ``django.views.defaults.shortcut()``. This function has been moved
to ``django.contrib.contenttypes.views.shortcut()`` as part of the to ``django.contrib.contenttypes.views.shortcut()`` as part of the

View File

@ -106,6 +106,23 @@ If you are currently using the ``mod_python`` request handler, it is strongly
encouraged you redeploy your Django instances using :doc:`mod_wsgi encouraged you redeploy your Django instances using :doc:`mod_wsgi
</howto/deployment/modwsgi>`. </howto/deployment/modwsgi>`.
Test client response ``template`` attribute
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Django's :ref:`test client <test-client>` returns
:class:`~django.test.client.Response` objects annotated with extra testing
information. In Django versions prior to 1.3, this included a
:attr:`~django.test.client.Response.template` attribute containing information
about templates rendered in generating the response: either None, a single
:class:`~django.template.Template` object, or a list of
:class:`~django.template.Template` objects. This inconsistency in return values
(sometimes a list, sometimes not) made the attribute difficult to work with.
In Django 1.3 the :attr:`~django.test.client.Response.template` attribute is
deprecated in favor of a new :attr:`~django.test.client.Response.templates`
attribute, which is always a list, even if it has only a single element or no
elements.
What's new in Django 1.3 What's new in Django 1.3
======================== ========================

View File

@ -494,6 +494,8 @@ Testing tools
Django provides a small set of tools that come in handy when writing tests. Django provides a small set of tools that come in handy when writing tests.
.. _test-client:
The test client The test client
--------------- ---------------
@ -894,15 +896,15 @@ Specifically, a ``Response`` object has the following attributes:
The HTTP status of the response, as an integer. See RFC2616_ for a full The HTTP status of the response, as an integer. See RFC2616_ for a full
list of HTTP status codes. list of HTTP status codes.
.. attribute:: template .. versionadded:: 1.3
The ``Template`` instance that was used to render the final content. Use .. attribute:: templates
A list of ``Template`` instances used to render the final content, in
the order they were rendered. For each template in the list, use
``template.name`` to get the template's file name, if the template was ``template.name`` to get the template's file name, if the template was
loaded from a file. (The name is a string such as ``'admin/index.html'``.) loaded from a file. (The name is a string such as
``'admin/index.html'``.)
If the rendered page used multiple templates -- e.g., using :ref:`template
inheritance<template-inheritance>` -- then ``template`` will be a list of
``Template`` instances, in the order in which they were rendered.
You can also use dictionary syntax on the response object to query the value You can also use dictionary syntax on the response object to query the value
of any settings in the HTTP headers. For example, you could determine the of any settings in the HTTP headers. For example, you could determine the

View File

@ -37,7 +37,7 @@ class ClientTest(TestCase):
# Check some response details # Check some response details
self.assertContains(response, 'This is a test') self.assertContains(response, 'This is a test')
self.assertEqual(response.context['var'], u'\xf2') self.assertEqual(response.context['var'], u'\xf2')
self.assertEqual(response.template.name, 'GET Template') self.assertEqual(response.templates[0].name, 'GET Template')
def test_get_post_view(self): def test_get_post_view(self):
"GET a view that normally expects POSTs" "GET a view that normally expects POSTs"
@ -45,7 +45,7 @@ class ClientTest(TestCase):
# Check some response details # Check some response details
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
self.assertEqual(response.template.name, 'Empty GET Template') self.assertEqual(response.templates[0].name, 'Empty GET Template')
self.assertTemplateUsed(response, 'Empty GET Template') self.assertTemplateUsed(response, 'Empty GET Template')
self.assertTemplateNotUsed(response, 'Empty POST Template') self.assertTemplateNotUsed(response, 'Empty POST Template')
@ -55,7 +55,7 @@ class ClientTest(TestCase):
# Check some response details # Check some response details
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
self.assertEqual(response.template.name, 'Empty POST Template') self.assertEqual(response.templates[0].name, 'Empty POST Template')
self.assertTemplateNotUsed(response, 'Empty GET Template') self.assertTemplateNotUsed(response, 'Empty GET Template')
self.assertTemplateUsed(response, 'Empty POST Template') self.assertTemplateUsed(response, 'Empty POST Template')
@ -69,7 +69,7 @@ class ClientTest(TestCase):
# Check some response details # Check some response details
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
self.assertEqual(response.context['data'], '37') self.assertEqual(response.context['data'], '37')
self.assertEqual(response.template.name, 'POST Template') self.assertEqual(response.templates[0].name, 'POST Template')
self.failUnless('Data received' in response.content) self.failUnless('Data received' in response.content)
def test_response_headers(self): def test_response_headers(self):
@ -84,7 +84,7 @@ class ClientTest(TestCase):
response = self.client.post("/test_client/raw_post_view/", test_doc, response = self.client.post("/test_client/raw_post_view/", test_doc,
content_type="text/xml") content_type="text/xml")
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
self.assertEqual(response.template.name, "Book template") self.assertEqual(response.templates[0].name, "Book template")
self.assertEqual(response.content, "Blink - Malcolm Gladwell") self.assertEqual(response.content, "Blink - Malcolm Gladwell")
def test_redirect(self): def test_redirect(self):

View File

@ -9,7 +9,7 @@ from django.test import Client, TestCase
from django.test.utils import ContextList from django.test.utils import ContextList
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django.core.exceptions import SuspiciousOperation from django.core.exceptions import SuspiciousOperation
from django.template import TemplateDoesNotExist, TemplateSyntaxError, Context from django.template import TemplateDoesNotExist, TemplateSyntaxError, Context, Template
from django.template import loader from django.template import loader
from django.test.client import encode_file from django.test.client import encode_file
@ -861,3 +861,18 @@ class RequestHeadersTest(TestCase):
self.assertEquals(response.content, "HTTP_X_ARG_CHECK: Testing 123") self.assertEquals(response.content, "HTTP_X_ARG_CHECK: Testing 123")
self.assertRedirects(response, '/test_client_regress/check_headers/', self.assertRedirects(response, '/test_client_regress/check_headers/',
status_code=301, target_status_code=200) status_code=301, target_status_code=200)
class ResponseTemplateDeprecationTests(TestCase):
"""
Response.template still works backwards-compatibly, but with pending deprecation warning. Refs #12226.
"""
def test_response_template_data(self):
response = self.client.get("/test_client_regress/request_data/", data={'foo':'whiz'})
self.assertEqual(response.template.__class__, Template)
self.assertEqual(response.template.name, 'base.html')
def test_response_no_template(self):
response = self.client.get("/test_client_regress/request_methods/")
self.assertEqual(response.template, None)