diff --git a/django/test/testcases.py b/django/test/testcases.py index 95d0d8e399..a7986dd028 100644 --- a/django/test/testcases.py +++ b/django/test/testcases.py @@ -521,7 +521,7 @@ class SimpleTestCase(unittest.TestCase): None] return None, template_names, msg_prefix - def assertTemplateUsed(self, response=None, template_name=None, msg_prefix=''): + def assertTemplateUsed(self, response=None, template_name=None, msg_prefix='', count=None): """ Asserts that the template with the provided name was used in rendering the response. Also usable as context manager. @@ -540,6 +540,12 @@ class SimpleTestCase(unittest.TestCase): " the response. Actual template(s) used: %s" % (template_name, ', '.join(template_names))) + if count is not None: + self.assertEqual(template_names.count(template_name), count, + msg_prefix + "Template '%s' was expected to be rendered %d " + "time(s) but was actually rendered %d time(s)." % + (template_name, count, template_names.count(template_name))) + def assertTemplateNotUsed(self, response=None, template_name=None, msg_prefix=''): """ Asserts that the template with the provided name was NOT used in diff --git a/docs/releases/1.8.txt b/docs/releases/1.8.txt index 977b166880..b206dcc4ce 100644 --- a/docs/releases/1.8.txt +++ b/docs/releases/1.8.txt @@ -154,7 +154,9 @@ Requests and Responses Tests ^^^^^ -* ... +* The ``count`` argument was added to + :meth:`~django.test.SimpleTestCase.assertTemplateUsed`. This allows you to + assert that a template was rendered a specific number of times. Validators ^^^^^^^^^^ diff --git a/docs/topics/testing/tools.txt b/docs/topics/testing/tools.txt index 7bd37c63d6..2be5b8ac9f 100644 --- a/docs/topics/testing/tools.txt +++ b/docs/topics/testing/tools.txt @@ -1328,13 +1328,19 @@ your test suite. attribute ordering is not significant. See :meth:`~SimpleTestCase.assertHTMLEqual` for more details. -.. method:: SimpleTestCase.assertTemplateUsed(response, template_name, msg_prefix='') +.. method:: SimpleTestCase.assertTemplateUsed(response, template_name, msg_prefix='', count=None) Asserts that the template with the given name was used in rendering the response. The name is a string such as ``'admin/index.html'``. + .. versionadded:: 1.8 + + The count argument is an integer indicating the number of times the + template should be rendered. Default is ``None``, meaning that the + template should be rendered one or more times. + You can use this as a context manager, like this:: with self.assertTemplateUsed('index.html'): diff --git a/tests/test_client_regress/tests.py b/tests/test_client_regress/tests.py index 59cc46b567..da903dfcfd 100644 --- a/tests/test_client_regress/tests.py +++ b/tests/test_client_regress/tests.py @@ -213,6 +213,12 @@ class AssertTemplateUsedTests(TestCase): except AssertionError as e: self.assertIn("abc: No templates used to render the response", str(e)) + with self.assertRaises(AssertionError) as context: + self.assertTemplateUsed(response, 'GET Template', count=2) + self.assertIn( + "No templates used to render the response", + str(context.exception)) + def test_single_context(self): "Template assertions work when there is a single context" response = self.client.get('/post_view/', {}) @@ -237,6 +243,21 @@ class AssertTemplateUsedTests(TestCase): except AssertionError as e: self.assertIn("abc: Template 'Empty POST Template' was not a template used to render the response. Actual template(s) used: Empty GET Template", str(e)) + with self.assertRaises(AssertionError) as context: + self.assertTemplateUsed(response, 'Empty GET Template', count=2) + self.assertIn( + "Template 'Empty GET Template' was expected to be rendered 2 " + "time(s) but was actually rendered 1 time(s).", + str(context.exception)) + + with self.assertRaises(AssertionError) as context: + self.assertTemplateUsed( + response, 'Empty GET Template', msg_prefix='abc', count=2) + self.assertIn( + "abc: Template 'Empty GET Template' was expected to be rendered 2 " + "time(s) but was actually rendered 1 time(s).", + str(context.exception)) + def test_multiple_context(self): "Template assertions work when there are multiple contexts" post_data = { @@ -263,6 +284,19 @@ class AssertTemplateUsedTests(TestCase): except AssertionError as e: self.assertIn("Template 'Valid POST Template' was not a template used to render the response. Actual template(s) used: form_view.html, base.html", str(e)) + with self.assertRaises(AssertionError) as context: + self.assertTemplateUsed(response, 'base.html', count=2) + self.assertIn( + "Template 'base.html' was expected to be rendered 2 " + "time(s) but was actually rendered 1 time(s).", + str(context.exception)) + + def test_template_rendered_multiple_times(self): + """Template assertions work when a template is rendered multiple times.""" + response = self.client.get('/render_template_multiple_times/') + + self.assertTemplateUsed(response, 'base.html', count=2) + @override_settings(ROOT_URLCONF='test_client_regress.urls') class AssertRedirectsTests(TestCase): diff --git a/tests/test_client_regress/urls.py b/tests/test_client_regress/urls.py index 44bbebfcd8..1bd0e0bc5b 100644 --- a/tests/test_client_regress/urls.py +++ b/tests/test_client_regress/urls.py @@ -37,4 +37,5 @@ urlpatterns = [ url(r'^read_all/$', views.read_all), url(r'^read_buffer/$', views.read_buffer), url(r'^request_context_view/$', views.request_context_view), + url(r'^render_template_multiple_times/$', views.render_template_multiple_times), ] diff --git a/tests/test_client_regress/views.py b/tests/test_client_regress/views.py index db5de3bf87..656d7a4651 100644 --- a/tests/test_client_regress/views.py +++ b/tests/test_client_regress/views.py @@ -7,6 +7,7 @@ from django.http import HttpResponse, HttpResponseRedirect from django.shortcuts import render_to_response from django.core.serializers.json import DjangoJSONEncoder from django.template import RequestContext +from django.template.loader import render_to_string from django.test import Client from django.test.client import CONTENT_TYPE_RE from django.test.utils import setup_test_environment @@ -151,3 +152,9 @@ def request_context_view(request): # Special attribute that won't be present on a plain HttpRequest request.special_path = request.path return render_to_response('request_context.html', context_instance=RequestContext(request, {})) + + +def render_template_multiple_times(request): + """A view that renders a template multiple times.""" + return HttpResponse( + render_to_string('base.html') + render_to_string('base.html'))