From 95827452571eb976c4f0d5e9ac46843948dd5fe6 Mon Sep 17 00:00:00 2001 From: Adam Johnson Date: Mon, 22 Jul 2024 11:19:00 +0100 Subject: [PATCH] Fixed #35622 -- Made unittest ignore Django assertions in traceback frames. Co-authored-by: Natalia <124304+nessita@users.noreply.github.com> --- django/contrib/admin/tests.py | 3 +++ django/contrib/messages/test.py | 3 +++ django/test/testcases.py | 3 +++ docs/releases/5.2.txt | 4 +++- tests/messages_tests/tests.py | 16 ++++++++++++++++ tests/test_utils/tests.py | 14 ++++++++++++++ 6 files changed, 42 insertions(+), 1 deletion(-) diff --git a/django/contrib/admin/tests.py b/django/contrib/admin/tests.py index dc857055b1..3810c359e5 100644 --- a/django/contrib/admin/tests.py +++ b/django/contrib/admin/tests.py @@ -6,6 +6,9 @@ from django.test.selenium import SeleniumTestCase from django.utils.deprecation import MiddlewareMixin from django.utils.translation import gettext as _ +# Make unittest ignore frames in this module when reporting failures. +__unittest = True + class CSPMiddleware(MiddlewareMixin): """The admin's JavaScript should be compatible with CSP.""" diff --git a/django/contrib/messages/test.py b/django/contrib/messages/test.py index 3a69f54585..9f3e1bf0bc 100644 --- a/django/contrib/messages/test.py +++ b/django/contrib/messages/test.py @@ -1,5 +1,8 @@ from .api import get_messages +# Make unittest ignore frames in this module when reporting failures. +__unittest = True + class MessagesTestMixin: def assertMessages(self, response, expected_messages, *, ordered=True): diff --git a/django/test/testcases.py b/django/test/testcases.py index 6027332cd5..cd7e7b45d6 100644 --- a/django/test/testcases.py +++ b/django/test/testcases.py @@ -67,6 +67,9 @@ __all__ = ( "skipUnlessDBFeature", ) +# Make unittest ignore frames in this module when reporting failures. +__unittest = True + if not PY311: # Backport of unittest.case._enter_context() from Python 3.11. diff --git a/docs/releases/5.2.txt b/docs/releases/5.2.txt index 92bb501d61..5732a5473c 100644 --- a/docs/releases/5.2.txt +++ b/docs/releases/5.2.txt @@ -240,7 +240,9 @@ Templates Tests ~~~~~ -* ... +* Stack frames from Django's custom assertions are now hidden. This makes test + failures easier to read and enables :option:`test --pdb` to directly enter + into the failing test method. URLs ~~~~ diff --git a/tests/messages_tests/tests.py b/tests/messages_tests/tests.py index 19aeee9a08..3f5cd56e85 100644 --- a/tests/messages_tests/tests.py +++ b/tests/messages_tests/tests.py @@ -1,5 +1,7 @@ import importlib import sys +import traceback +import unittest from unittest import mock from django.conf import settings @@ -185,3 +187,17 @@ class AssertMessagesTest(MessagesTestMixin, SimpleTestCase): ) with self.assertRaisesMessage(AssertionError, msg): self.assertMessages(response, []) + + def test_method_frames_ignored_by_unittest(self): + response = FakeResponse() + try: + self.assertMessages(response, [object()]) + except AssertionError: + exc_type, exc, tb = sys.exc_info() + + result = unittest.TestResult() + result.addFailure(self, (exc_type, exc, tb)) + stack = traceback.extract_tb(exc.__traceback__) + self.assertEqual(len(stack), 1) + # Top element in the stack is this method, not assertMessages. + self.assertEqual(stack[-1].name, "test_method_frames_ignored_by_unittest") diff --git a/tests/test_utils/tests.py b/tests/test_utils/tests.py index cd64c087c4..60b65e309a 100644 --- a/tests/test_utils/tests.py +++ b/tests/test_utils/tests.py @@ -1,6 +1,7 @@ import os import sys import threading +import traceback import unittest import warnings from io import StringIO @@ -1113,6 +1114,19 @@ class JSONEqualTests(SimpleTestCase): with self.assertRaises(AssertionError): self.assertJSONNotEqual(valid_json, invalid_json) + def test_method_frames_ignored_by_unittest(self): + try: + self.assertJSONEqual("1", "2") + except AssertionError: + exc_type, exc, tb = sys.exc_info() + + result = unittest.TestResult() + result.addFailure(self, (exc_type, exc, tb)) + stack = traceback.extract_tb(exc.__traceback__) + self.assertEqual(len(stack), 1) + # Top element in the stack is this method, not assertJSONEqual. + self.assertEqual(stack[-1].name, "test_method_frames_ignored_by_unittest") + class XMLEqualTests(SimpleTestCase): def test_simple_equal(self):