From ee2f04d79e5bca55637b9bb3301618738a4e342a Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Wed, 18 Mar 2009 10:46:55 +0000 Subject: [PATCH] Fixed #10482 -- Unified access to response.context when inspecting responses from the test client. Thanks to James Bennett for the design, and Julien Phalip for the patch. git-svn-id: http://code.djangoproject.com/svn/django/trunk@10084 bcc190cf-cafb-0310-a4f2-bffc1f526a37 --- django/test/client.py | 5 ++-- django/test/utils.py | 14 +++++++++++ docs/topics/testing.txt | 10 ++++++++ .../test_client_regress/models.py | 23 ++++++++++++++++++- .../test_client_regress/urls.py | 1 + .../test_client_regress/views.py | 5 ++-- tests/templates/extended.html | 5 ++++ 7 files changed, 58 insertions(+), 5 deletions(-) create mode 100644 tests/templates/extended.html diff --git a/django/test/client.py b/django/test/client.py index 20283b7b3b..6c5f4ff4a9 100644 --- a/django/test/client.py +++ b/django/test/client.py @@ -20,6 +20,7 @@ from django.utils.encoding import smart_str from django.utils.http import urlencode from django.utils.itercompat import is_iterable from django.db import transaction, close_connection +from django.test.utils import ContextList BOUNDARY = 'BoUnDaRyStRiNg' MULTIPART_CONTENT = 'multipart/form-data; boundary=%s' % BOUNDARY @@ -80,8 +81,8 @@ def store_rendered_templates(store, signal, sender, template, context, **kwargs) """ Stores templates and contexts that are rendered. """ - store.setdefault('template',[]).append(template) - store.setdefault('context',[]).append(context) + store.setdefault('template', []).append(template) + store.setdefault('context', ContextList()).append(context) def encode_multipart(boundary, data): """ diff --git a/django/test/utils.py b/django/test/utils.py index 29babec3ff..d34dd33d15 100644 --- a/django/test/utils.py +++ b/django/test/utils.py @@ -6,6 +6,20 @@ from django.test import signals from django.template import Template from django.utils.translation import deactivate +class ContextList(list): + """A wrapper that provides direct key access to context items contained + in a list of context objects. + """ + def __getitem__(self, key): + if isinstance(key, basestring): + for subcontext in self: + if key in subcontext: + return subcontext[key] + raise KeyError + else: + return super(ContextList, self).__getitem__(key) + + def instrumented_test_render(self, context): """ An instrumented Template render method, providing a signal diff --git a/docs/topics/testing.txt b/docs/topics/testing.txt index e1c3c2e06c..974856b0c4 100644 --- a/docs/topics/testing.txt +++ b/docs/topics/testing.txt @@ -712,6 +712,16 @@ Specifically, a ``Response`` object has the following attributes: If the rendered page used multiple templates, then ``context`` will be a list of ``Context`` objects, in the order in which they were rendered. + .. versionadded:: 1.1 + + Regardless of the number of templates used during rendering, you can + retrieve context values using the ``[]`` operator. For example, the + context variable ``name`` could be retrieved using:: + + >>> response = client.get('/foo/') + >>> response.context['name'] + 'Arthur' + .. attribute:: request The request data that stimulated the response. diff --git a/tests/regressiontests/test_client_regress/models.py b/tests/regressiontests/test_client_regress/models.py index 16c26365dd..5d650923a4 100644 --- a/tests/regressiontests/test_client_regress/models.py +++ b/tests/regressiontests/test_client_regress/models.py @@ -5,9 +5,10 @@ import os from django.conf import settings from django.test import Client, TestCase +from django.test.utils import ContextList from django.core.urlresolvers import reverse from django.core.exceptions import SuspiciousOperation -from django.template import TemplateDoesNotExist, TemplateSyntaxError +from django.template import TemplateDoesNotExist, TemplateSyntaxError, Context class AssertContainsTests(TestCase): def test_contains(self): @@ -455,6 +456,26 @@ class zzUrlconfSubstitutionTests(TestCase): url = reverse('arg_view', args=['somename']) self.assertEquals(url, '/test_client_regress/arg_view/somename/') +class ContextTests(TestCase): + fixtures = ['testdata'] + + def test_single_context(self): + "Context variables can be retrieved from a single context" + response = self.client.get("/test_client_regress/request_data/", data={'foo':'whiz'}) + self.assertEqual(response.context.__class__, Context) + self.assertEqual(response.context['get-foo'], 'whiz') + self.assertEqual(response.context['request-foo'], 'whiz') + self.assertEqual(response.context['data'], 'sausage') + + def test_inherited_context(self): + "Context variables can be retrieved from a list of contexts" + response = self.client.get("/test_client_regress/request_data_extended/", data={'foo':'whiz'}) + self.assertEqual(response.context.__class__, ContextList) + self.assertEqual(len(response.context), 2) + self.assertEqual(response.context['get-foo'], 'whiz') + self.assertEqual(response.context['request-foo'], 'whiz') + self.assertEqual(response.context['data'], 'bacon') + class SessionTests(TestCase): fixtures = ['testdata.json'] diff --git a/tests/regressiontests/test_client_regress/urls.py b/tests/regressiontests/test_client_regress/urls.py index 9e573f6384..0f9082d4cb 100644 --- a/tests/regressiontests/test_client_regress/urls.py +++ b/tests/regressiontests/test_client_regress/urls.py @@ -7,6 +7,7 @@ urlpatterns = patterns('', (r'^staff_only/$', views.staff_only_view), (r'^get_view/$', views.get_view), (r'^request_data/$', views.request_data), + (r'^request_data_extended/$', views.request_data, {'template':'extended.html', 'data':'bacon'}), url(r'^arg_view/(?P.+)/$', views.view_with_argument, name='arg_view'), (r'^login_protected_redirect_view/$', views.login_protected_redirect_view), (r'^redirects/$', redirect_to, {'url': '/test_client_regress/redirects/further/'}), diff --git a/tests/regressiontests/test_client_regress/views.py b/tests/regressiontests/test_client_regress/views.py index 453c37a32e..bd0b8aff50 100644 --- a/tests/regressiontests/test_client_regress/views.py +++ b/tests/regressiontests/test_client_regress/views.py @@ -19,15 +19,16 @@ def get_view(request): return HttpResponse("Hello world") get_view = login_required(get_view) -def request_data(request): +def request_data(request, template='base.html', data='sausage'): "A simple view that returns the request data in the context" - return render_to_response('base.html', { + return render_to_response(template, { 'get-foo':request.GET.get('foo',None), 'get-bar':request.GET.get('bar',None), 'post-foo':request.POST.get('foo',None), 'post-bar':request.POST.get('bar',None), 'request-foo':request.REQUEST.get('foo',None), 'request-bar':request.REQUEST.get('bar',None), + 'data': data, }) def view_with_argument(request, name): diff --git a/tests/templates/extended.html b/tests/templates/extended.html new file mode 100644 index 0000000000..e0d8a13727 --- /dev/null +++ b/tests/templates/extended.html @@ -0,0 +1,5 @@ +{% extends "base.html" %} +{% block title %}Extended template{% endblock %} +{% block content %} +This is just a template extending the base. +{% endblock %} \ No newline at end of file