Browse Source

Refs #21429 -- Added SimpleTestCase.assertNoLogs() on Python < 3.10.

François Freitag 4 years ago
parent
commit
7ca7f4495b

+ 25 - 0
django/test/testcases.py

@@ -1,6 +1,7 @@
 import asyncio
 import difflib
 import json
+import logging
 import posixpath
 import sys
 import threading
@@ -43,6 +44,7 @@ from django.test.utils import (
 )
 from django.utils.deprecation import RemovedInDjango41Warning
 from django.utils.functional import classproperty
+from django.utils.version import PY310
 from django.views.static import serve
 
 __all__ = ('TestCase', 'TransactionTestCase',
@@ -730,6 +732,29 @@ class SimpleTestCase(unittest.TestCase):
             *args, **kwargs
         )
 
+    # A similar method is available in Python 3.10+.
+    if not PY310:
+        @contextmanager
+        def assertNoLogs(self, logger, level=None):
+            """
+            Assert no messages are logged on the logger, with at least the
+            given level.
+            """
+            if isinstance(level, int):
+                level = logging.getLevelName(level)
+            elif level is None:
+                level = 'INFO'
+            try:
+                with self.assertLogs(logger, level) as cm:
+                    yield
+            except AssertionError as e:
+                msg = e.args[0]
+                expected_msg = f'no logs of level {level} or higher triggered on {logger}'
+                if msg != expected_msg:
+                    raise e
+            else:
+                self.fail(f'Unexpected logs found: {cm.output!r}')
+
     def assertFieldOutput(self, fieldclass, valid, invalid, field_args=None,
                           field_kwargs=None, empty_value=''):
         """

+ 1 - 0
django/utils/version.py

@@ -13,6 +13,7 @@ PY36 = sys.version_info >= (3, 6)
 PY37 = sys.version_info >= (3, 7)
 PY38 = sys.version_info >= (3, 8)
 PY39 = sys.version_info >= (3, 9)
+PY310 = sys.version_info >= (3, 10)
 
 
 def get_version(version=None):

+ 2 - 3
tests/admin_views/test_nav_sidebar.py

@@ -82,9 +82,8 @@ class AdminSidebarTests(TestCase):
     def test_included_app_list_template_context_fully_set(self):
         # All context variables should be set when rendering the sidebar.
         url = reverse('test_with_sidebar:auth_user_changelist')
-        with self.assertRaisesMessage(AssertionError, 'no logs'):
-            with self.assertLogs('django.template', 'DEBUG'):
-                self.client.get(url)
+        with self.assertNoLogs('django.template', 'DEBUG'):
+            self.client.get(url)
 
 
 @override_settings(ROOT_URLCONF='admin_views.test_nav_sidebar')

+ 3 - 4
tests/csrf_tests/tests.py

@@ -454,10 +454,9 @@ class CsrfViewMiddlewareTestMixin:
         """
         ensure_csrf_cookie() doesn't log warnings (#19436).
         """
-        with self.assertRaisesMessage(AssertionError, 'no logs'):
-            with self.assertLogs('django.request', 'WARNING'):
-                req = self._get_GET_no_csrf_cookie_request()
-                ensure_csrf_cookie_view(req)
+        with self.assertNoLogs('django.request', 'WARNING'):
+            req = self._get_GET_no_csrf_cookie_request()
+            ensure_csrf_cookie_view(req)
 
     def test_post_data_read_failure(self):
         """

+ 2 - 3
tests/gis_tests/geoadmin/tests.py

@@ -72,9 +72,8 @@ class GeoAdminTest(SimpleTestCase):
     def test_olwidget_empty_string(self):
         geoadmin = site._registry[City]
         form = geoadmin.get_changelist_form(None)({'point': ''})
-        with self.assertRaisesMessage(AssertionError, 'no logs'):
-            with self.assertLogs('django.contrib.gis', 'ERROR'):
-                output = str(form['point'])
+        with self.assertNoLogs('django.contrib.gis', 'ERROR'):
+            output = str(form['point'])
         self.assertInHTML(
             '<textarea id="id_point" class="vWKTField required" cols="150"'
             ' rows="10" name="point"></textarea>',

+ 2 - 3
tests/gis_tests/geoapp/tests.py

@@ -427,9 +427,8 @@ class GeoLookupTest(TestCase):
 
     def test_wkt_string_in_lookup(self):
         # Valid WKT strings don't emit error logs.
-        with self.assertRaisesMessage(AssertionError, 'no logs'):
-            with self.assertLogs('django.contrib.gis', 'ERROR'):
-                State.objects.filter(poly__intersects='LINESTRING(0 0, 1 1, 5 5)')
+        with self.assertNoLogs('django.contrib.gis', 'ERROR'):
+            State.objects.filter(poly__intersects='LINESTRING(0 0, 1 1, 5 5)')
 
     @skipUnlessDBFeature("supports_relate_lookup")
     def test_relate_lookup(self):

+ 2 - 3
tests/middleware_exceptions/tests.py

@@ -177,9 +177,8 @@ class MiddlewareNotUsedTests(SimpleTestCase):
         MIDDLEWARE=['middleware_exceptions.tests.MyMiddleware'],
     )
     def test_do_not_log_when_debug_is_false(self):
-        with self.assertRaisesMessage(AssertionError, 'no logs'):
-            with self.assertLogs('django.request', 'DEBUG'):
-                self.client.get('/middleware_exceptions/view/')
+        with self.assertNoLogs('django.request', 'DEBUG'):
+            self.client.get('/middleware_exceptions/view/')
 
     @override_settings(MIDDLEWARE=[
         'middleware_exceptions.middleware.SyncAndAsyncMiddleware',

+ 2 - 3
tests/template_tests/test_logging.py

@@ -62,6 +62,5 @@ class VariableResolveLoggingTests(SimpleTestCase):
         )
 
     def test_no_log_when_variable_exists(self):
-        with self.assertRaisesMessage(AssertionError, 'no logs'):
-            with self.assertLogs('django.template', self.loglevel):
-                Variable('article.section').resolve({'article': {'section': 'News'}})
+        with self.assertNoLogs('django.template', self.loglevel):
+            Variable('article.section').resolve({'article': {'section': 'News'}})

+ 43 - 0
tests/test_utils/tests.py

@@ -1,3 +1,4 @@
+import logging
 import os
 import unittest
 import warnings
@@ -26,6 +27,7 @@ from django.test.utils import (
 )
 from django.urls import NoReverseMatch, path, reverse, reverse_lazy
 from django.utils.deprecation import RemovedInDjango41Warning
+from django.utils.log import DEFAULT_LOGGING
 
 from .models import Car, Person, PossessedCar
 from .views import empty_response
@@ -1105,6 +1107,47 @@ class AssertWarnsMessageTests(SimpleTestCase):
             func1()
 
 
+# TODO: Remove when dropping support for PY39.
+class AssertNoLogsTest(SimpleTestCase):
+    @classmethod
+    def setUpClass(cls):
+        super().setUpClass()
+        logging.config.dictConfig(DEFAULT_LOGGING)
+        cls.addClassCleanup(logging.config.dictConfig, settings.LOGGING)
+
+    def setUp(self):
+        self.logger = logging.getLogger('django')
+
+    @override_settings(DEBUG=True)
+    def test_fails_when_log_emitted(self):
+        msg = "Unexpected logs found: ['INFO:django:FAIL!']"
+        with self.assertRaisesMessage(AssertionError, msg):
+            with self.assertNoLogs('django', 'INFO'):
+                self.logger.info('FAIL!')
+
+    @override_settings(DEBUG=True)
+    def test_text_level(self):
+        with self.assertNoLogs('django', 'INFO'):
+            self.logger.debug('DEBUG logs are ignored.')
+
+    @override_settings(DEBUG=True)
+    def test_int_level(self):
+        with self.assertNoLogs('django', logging.INFO):
+            self.logger.debug('DEBUG logs are ignored.')
+
+    @override_settings(DEBUG=True)
+    def test_default_level(self):
+        with self.assertNoLogs('django'):
+            self.logger.debug('DEBUG logs are ignored.')
+
+    @override_settings(DEBUG=True)
+    def test_does_not_hide_other_failures(self):
+        msg = '1 != 2'
+        with self.assertRaisesMessage(AssertionError, msg):
+            with self.assertNoLogs('django'):
+                self.assertEqual(1, 2)
+
+
 class AssertFieldOutputTests(SimpleTestCase):
 
     def test_assert_field_output(self):