From 024ea0f78322eda09e245b0facb8ba4296408e25 Mon Sep 17 00:00:00 2001 From: Clifford Gama Date: Tue, 24 Jun 2025 21:33:37 +0200 Subject: [PATCH 1/2] Split monolithic AssertContainsTests.test_contains() into focused tests. Added a local helper to test assertion messages with and without a given msg_prefix. --- tests/test_client_regress/tests.py | 308 +++++++++++++++-------------- 1 file changed, 160 insertions(+), 148 deletions(-) diff --git a/tests/test_client_regress/tests.py b/tests/test_client_regress/tests.py index b8efdca4f253..bc18b11a452c 100644 --- a/tests/test_client_regress/tests.py +++ b/tests/test_client_regress/tests.py @@ -35,167 +35,179 @@ def setUpTestData(cls): ) +class ExtraAssertMixin: + + def assertRaisesPrefixedMessage( + self, + method, + *method_args, + expected_msg, + msg_prefix="abc", + **method_kwargs, + ): + """ + `method` raises an AssertionError with and without a prefixed message. + + :param method: The assertion method to test. + :param method_args: Positional arguments to pass to the method. + :param expected_msg: The expected base error message (required keyword-only). + :param msg_prefix: Optional prefix to be added to the message in the second + subTest. + :param method_kwargs: Keyword arguments to pass to the method. + + Used internally for testing Django's assertions. + """ + with ( + self.subTest("without prefix"), + self.assertRaisesMessage(AssertionError, expected_msg), + ): + method(*method_args, **method_kwargs) + + with ( + self.subTest("with prefix"), + self.assertRaisesMessage(AssertionError, f"{msg_prefix}: {expected_msg}"), + ): + method(*method_args, **method_kwargs, msg_prefix=msg_prefix) + + @override_settings(ROOT_URLCONF="test_client_regress.urls") -class AssertContainsTests(SimpleTestCase): - def test_contains(self): - "Responses can be inspected for content, including counting repeated substrings" +class AssertContainsTests(ExtraAssertMixin, SimpleTestCase): + + def test_basic_contains_not_contains(self): response = self.client.get("/no_template_view/") - self.assertNotContains(response, "never") - self.assertContains(response, "never", 0) - self.assertContains(response, "once") - self.assertContains(response, "once", 1) - self.assertContains(response, "twice") - self.assertContains(response, "twice", 2) + with self.subTest("assertNotContains"): + self.assertNotContains(response, "never") - try: - self.assertContains(response, "text", status_code=999) - except AssertionError as e: - self.assertIn( - "Couldn't retrieve content: Response code was 200 (expected 999)", - str(e), - ) - try: - self.assertContains(response, "text", status_code=999, msg_prefix="abc") - except AssertionError as e: - self.assertIn( - "abc: Couldn't retrieve content: Response code was 200 (expected 999)", - str(e), - ) + cases = [ + ("never", 0), + ("once", None), + ("once", 1), + ("twice", None), + ("twice", 2), + ] - try: - self.assertNotContains(response, "text", status_code=999) - except AssertionError as e: - self.assertIn( - "Couldn't retrieve content: Response code was 200 (expected 999)", - str(e), - ) - try: - self.assertNotContains(response, "text", status_code=999, msg_prefix="abc") - except AssertionError as e: - self.assertIn( - "abc: Couldn't retrieve content: Response code was 200 (expected 999)", - str(e), - ) + for text, expected_count in cases: + with self.subTest(text=text, expected_count=expected_count): + if expected_count is not None: + self.assertContains(response, text, count=expected_count) + else: + self.assertContains(response, text) - try: - self.assertNotContains(response, "once") - except AssertionError as e: - self.assertIn( - "'once' unexpectedly found in the following response\n" - f"{response.content}", - str(e), - ) - try: - self.assertNotContains(response, "once", msg_prefix="abc") - except AssertionError as e: - self.assertIn( - "abc: 'once' unexpectedly found in the following response\n" - f"{response.content}", - str(e), - ) + def test_contains_with_wrong_status_code(self): + response = self.client.get("/no_template_view/") + msg = "Couldn't retrieve content: Response code was 200 (expected 999)" + self.assertRaisesPrefixedMessage( + self.assertContains, + response, + "text", + status_code=999, + expected_msg=msg, + ) - try: - self.assertContains(response, "never", 1) - except AssertionError as e: - self.assertIn( - "Found 0 instances of 'never' (expected 1) in the following response\n" - f"{response.content}", - str(e), - ) - try: - self.assertContains(response, "never", 1, msg_prefix="abc") - except AssertionError as e: - self.assertIn( - "abc: Found 0 instances of 'never' (expected 1) in the following " - f"response\n{response.content}", - str(e), - ) + def test_not_contains_with_wrong_status_code(self): + response = self.client.get("/no_template_view/") + msg = "Couldn't retrieve content: Response code was 200 (expected 999)" + self.assertRaisesPrefixedMessage( + self.assertNotContains, + response, + "text", + status_code=999, + expected_msg=msg, + ) - try: - self.assertContains(response, "once", 0) - except AssertionError as e: - self.assertIn( - "Found 1 instances of 'once' (expected 0) in the following response\n" - f"{response.content}", - str(e), - ) - try: - self.assertContains(response, "once", 0, msg_prefix="abc") - except AssertionError as e: - self.assertIn( - "abc: Found 1 instances of 'once' (expected 0) in the following " - f"response\n{response.content}", - str(e), - ) + def test_not_contains_failure(self): + response = self.client.get("/no_template_view/") + msg = f"'once' unexpectedly found in the following response\n{response.content}" + self.assertRaisesPrefixedMessage( + self.assertNotContains, + response, + "once", + expected_msg=msg, + ) - try: - self.assertContains(response, "once", 2) - except AssertionError as e: - self.assertIn( - "Found 1 instances of 'once' (expected 2) in the following response\n" - f"{response.content}", - str(e), - ) - try: - self.assertContains(response, "once", 2, msg_prefix="abc") - except AssertionError as e: - self.assertIn( - "abc: Found 1 instances of 'once' (expected 2) in the following " - f"response\n{response.content}", - str(e), - ) + def test_count_mismatch(self): + response = self.client.get("/no_template_view/") + msg = ( + "Found 0 instances of 'never' (expected 1) in the following response\n" + f"{response.content}" + ) + self.assertRaisesPrefixedMessage( + self.assertContains, + response, + "never", + count=1, + expected_msg=msg, + ) - try: - self.assertContains(response, "twice", 1) - except AssertionError as e: - self.assertIn( - "Found 2 instances of 'twice' (expected 1) in the following response\n" - f"{response.content}", - str(e), - ) - try: - self.assertContains(response, "twice", 1, msg_prefix="abc") - except AssertionError as e: - self.assertIn( - "abc: Found 2 instances of 'twice' (expected 1) in the following " - f"response\n{response.content}", - str(e), - ) + def test_unexpected_presence(self): + response = self.client.get("/no_template_view/") + msg = ( + "Found 1 instances of 'once' (expected 0) in the following " + f"response\n{response.content}" + ) + self.assertRaisesPrefixedMessage( + self.assertContains, + response, + "once", + count=0, + expected_msg=msg, + ) - try: - self.assertContains(response, "thrice") - except AssertionError as e: - self.assertIn( - f"Couldn't find 'thrice' in the following response\n{response.content}", - str(e), - ) - try: - self.assertContains(response, "thrice", msg_prefix="abc") - except AssertionError as e: - self.assertIn( - "abc: Couldn't find 'thrice' in the following response\n" - f"{response.content}", - str(e), - ) + def test_insufficient_count(self): + response = self.client.get("/no_template_view/") + msg = ( + "Found 1 instances of 'once' (expected 2) in the following response\n" + f"{response.content}" + ) + self.assertRaisesPrefixedMessage( + self.assertContains, + response, + "once", + 2, + expected_msg=msg, + ) - try: - self.assertContains(response, "thrice", 3) - except AssertionError as e: - self.assertIn( - "Found 0 instances of 'thrice' (expected 3) in the following response\n" - f"{response.content}", - str(e), - ) - try: - self.assertContains(response, "thrice", 3, msg_prefix="abc") - except AssertionError as e: - self.assertIn( - "abc: Found 0 instances of 'thrice' (expected 3) in the following " - f"response\n{response.content}", - str(e), - ) + def test_excessive_count(self): + response = self.client.get("/no_template_view/") + msg = ( + "Found 2 instances of 'twice' (expected 1) in the following response\n" + f"{response.content}" + ) + self.assertRaisesPrefixedMessage( + self.assertContains, + response, + "twice", + count=1, + expected_msg=msg, + ) + + def test_missing_content(self): + response = self.client.get("/no_template_view/") + msg = f"Couldn't find 'thrice' in the following response\n{response.content}" + self.assertRaisesPrefixedMessage( + self.assertContains, + response, + "thrice", + expected_msg=msg, + msg_prefix="Custom prexix", + ) + + def test_missing_content_with_count(self): + response = self.client.get("/no_template_view/") + msg = ( + "Found 0 instances of 'thrice' (expected 3) in the following " + f"response\n{response.content}" + ) + self.assertRaisesPrefixedMessage( + self.assertContains, + response, + "thrice", + 3, + expected_msg=msg, + ) + def test_long_content(self): long_content = ( b"This is a very very very very very very very very long message which " b"exceeds the max limit of truncation." From 04e813cd17484ba91e3fc25d5e5118ac25f47352 Mon Sep 17 00:00:00 2001 From: Clifford Gama Date: Tue, 24 Jun 2025 22:35:45 +0200 Subject: [PATCH 2/2] Refs #4476 -- Added tests for assertRedirects() when following redirect chains. Thanks Natalia Bidart for the review. --- tests/test_client_regress/tests.py | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/tests/test_client_regress/tests.py b/tests/test_client_regress/tests.py index bc18b11a452c..a480a2069b54 100644 --- a/tests/test_client_regress/tests.py +++ b/tests/test_client_regress/tests.py @@ -385,7 +385,7 @@ def test_template_rendered_multiple_times(self): @override_settings(ROOT_URLCONF="test_client_regress.urls") -class AssertRedirectsTests(SimpleTestCase): +class AssertRedirectsTests(ExtraAssertMixin, SimpleTestCase): def test_redirect_page(self): "An assertion is raised if the original page couldn't be retrieved as expected" # This page will redirect with code 301, not 302 @@ -408,6 +408,34 @@ def test_redirect_page(self): str(e), ) + def test_followed_redirect_unexpected_initial_status_code(self): + response = self.client.get("/permanent_redirect_view/", follow=True) + msg = ( + "Initial response didn't redirect as expected: Response code was 301 " + "(expected 302)" + ) + self.assertRaisesPrefixedMessage( + self.assertRedirects, + response, + "/get_view/", + expected_msg=msg, + ) + + def test_followed_redirect_unexpected_final_status_code(self): + response = self.client.get("/redirect_view/", follow=True) + msg = ( + "Response didn't redirect as expected: Final Response code was 200 " + "(expected 403)" + ) + self.assertRaisesPrefixedMessage( + self.assertRedirects, + response, + "/get_view/", + status_code=302, + target_status_code=403, + expected_msg=msg, + ) + def test_lost_query(self): """ An assertion is raised if the redirect location doesn't preserve GET