From 9ac92b1efc5ff90e7cce5c47fbd05887d403e4cd Mon Sep 17 00:00:00 2001 From: Baptiste Mispelon Date: Sat, 20 Nov 2021 19:56:39 +0100 Subject: [PATCH] Refs #33301 -- Made SimpleTestCase.assertFormError()/assertFormsetErrors() raise ValueError for non test client responses. --- django/test/testcases.py | 28 +++++++++++++++++++--------- tests/test_utils/tests.py | 28 +++++++++++++++++++++------- 2 files changed, 40 insertions(+), 16 deletions(-) diff --git a/django/test/testcases.py b/django/test/testcases.py index 7b5c7cdd78..0e4d406464 100644 --- a/django/test/testcases.py +++ b/django/test/testcases.py @@ -477,11 +477,23 @@ class SimpleTestCase(unittest.TestCase): self.assertEqual(real_count, 0, msg_prefix + "Response should not contain %s" % text_repr) + def _check_test_client_response(self, response, attribute, method_name): + """ + Raise a ValueError if the given response doesn't have the required + attribute. + """ + if not hasattr(response, attribute): + raise ValueError( + f"{method_name}() is only usable on responses fetched using " + "the Django test Client." + ) + def assertFormError(self, response, form, field, errors, msg_prefix=''): """ Assert that a form used to render the response has a specific field error. """ + self._check_test_client_response(response, 'context', 'assertFormError') if msg_prefix: msg_prefix += ": " @@ -543,6 +555,7 @@ class SimpleTestCase(unittest.TestCase): For non-form errors, specify ``form_index`` as None and the ``field`` as None. """ + self._check_test_client_response(response, 'context', 'assertFormsetError') # Add punctuation to msg_prefix if msg_prefix: msg_prefix += ": " @@ -612,7 +625,7 @@ class SimpleTestCase(unittest.TestCase): if not found_formset: self.fail(msg_prefix + "The formset '%s' was not used to render the response" % formset) - def _assert_template_used(self, response, template_name, msg_prefix): + def _assert_template_used(self, response, template_name, msg_prefix, method_name): if response is None and template_name is None: raise TypeError('response and/or template_name argument must be provided') @@ -620,11 +633,8 @@ class SimpleTestCase(unittest.TestCase): if msg_prefix: msg_prefix += ": " - if template_name is not None and response is not None and not hasattr(response, 'templates'): - raise ValueError( - "assertTemplateUsed() and assertTemplateNotUsed() are only " - "usable on responses fetched using the Django test Client." - ) + if template_name is not None and response is not None: + self._check_test_client_response(response, 'templates', method_name) if not hasattr(response, 'templates') or (response is None and template_name): if response: @@ -642,8 +652,8 @@ class SimpleTestCase(unittest.TestCase): the response. Also usable as context manager. """ context_mgr_template, template_names, msg_prefix = self._assert_template_used( - response, template_name, msg_prefix) - + response, template_name, msg_prefix, 'assertTemplateUsed', + ) if context_mgr_template: # Use assertTemplateUsed as context manager. return _AssertTemplateUsedContext(self, context_mgr_template) @@ -671,7 +681,7 @@ class SimpleTestCase(unittest.TestCase): rendering the response. Also usable as context manager. """ context_mgr_template, template_names, msg_prefix = self._assert_template_used( - response, template_name, msg_prefix + response, template_name, msg_prefix, 'assertTemplateNotUsed', ) if context_mgr_template: # Use assertTemplateNotUsed as context manager. diff --git a/tests/test_utils/tests.py b/tests/test_utils/tests.py index fbff188576..47ce93e2ca 100644 --- a/tests/test_utils/tests.py +++ b/tests/test_utils/tests.py @@ -558,14 +558,10 @@ class AssertTemplateUsedContextManagerTests(SimpleTestCase): def test_assert_used_on_http_response(self): response = HttpResponse() - error_msg = ( - 'assertTemplateUsed() and assertTemplateNotUsed() are only ' - 'usable on responses fetched using the Django test Client.' - ) - with self.assertRaisesMessage(ValueError, error_msg): + msg = '%s() is only usable on responses fetched using the Django test Client.' + with self.assertRaisesMessage(ValueError, msg % 'assertTemplateUsed'): self.assertTemplateUsed(response, 'template.html') - - with self.assertRaisesMessage(ValueError, error_msg): + with self.assertRaisesMessage(ValueError, msg % 'assertTemplateNotUsed'): self.assertTemplateNotUsed(response, 'template.html') @@ -1288,6 +1284,15 @@ class TestFormset(formset_factory(TestForm)): class AssertFormErrorTests(SimpleTestCase): + def test_non_client_response(self): + msg = ( + 'assertFormError() is only usable on responses fetched using the ' + 'Django test Client.' + ) + response = HttpResponse() + with self.assertRaisesMessage(ValueError, msg): + self.assertFormError(response, 'formset', 0, 'field', 'invalid value') + def test_response_with_no_context(self): msg = 'Response did not use any contexts to render the response' response = mock.Mock(context=[]) @@ -1384,6 +1389,15 @@ class AssertFormsetErrorTests(SimpleTestCase): 'form-0-field': field_value, } + def test_non_client_response(self): + msg = ( + 'assertFormsetError() is only usable on responses fetched using ' + 'the Django test Client.' + ) + response = HttpResponse() + with self.assertRaisesMessage(ValueError, msg): + self.assertFormsetError(response, 'formset', 0, 'field', 'invalid value') + def test_response_with_no_context(self): msg = 'Response did not use any contexts to render the response' response = mock.Mock(context=[])