Fixed #32552 -- Added logger argument to DiscoverRunner.

This commit is contained in:
Chris Jerdonek 2021-08-09 13:18:51 -04:00 committed by Mariusz Felisiak
parent 022d29c934
commit b263f4b69d
4 changed files with 61 additions and 22 deletions

View File

@ -561,7 +561,7 @@ class DiscoverRunner:
reverse=False, debug_mode=False, debug_sql=False, parallel=0, reverse=False, debug_mode=False, debug_sql=False, parallel=0,
tags=None, exclude_tags=None, test_name_patterns=None, tags=None, exclude_tags=None, test_name_patterns=None,
pdb=False, buffer=False, enable_faulthandler=True, pdb=False, buffer=False, enable_faulthandler=True,
timing=False, shuffle=False, **kwargs): timing=False, shuffle=False, logger=None, **kwargs):
self.pattern = pattern self.pattern = pattern
self.top_level = top_level self.top_level = top_level
@ -595,6 +595,7 @@ class DiscoverRunner:
} }
self.shuffle = shuffle self.shuffle = shuffle
self._shuffler = None self._shuffler = None
self.logger = logger
@classmethod @classmethod
def add_arguments(cls, parser): def add_arguments(cls, parser):
@ -677,16 +678,23 @@ class DiscoverRunner:
def log(self, msg, level=None): def log(self, msg, level=None):
""" """
Log the given message at the given logging level. Log the message at the given logging level (the default is INFO).
A verbosity of 1 logs INFO (the default level) or above, and verbosity If a logger isn't set, the message is instead printed to the console,
2 or higher logs all levels. respecting the configured verbosity. A verbosity of 0 prints no output,
a verbosity of 1 prints INFO and above, and a verbosity of 2 or higher
prints all levels.
""" """
if level is None:
level = logging.INFO
if self.logger is None:
if self.verbosity <= 0 or ( if self.verbosity <= 0 or (
self.verbosity == 1 and level is not None and level < logging.INFO self.verbosity == 1 and level < logging.INFO
): ):
return return
print(msg) print(msg)
else:
self.logger.log(level, msg)
def setup_test_environment(self, **kwargs): def setup_test_environment(self, **kwargs):
setup_test_environment(debug=self.debug_mode) setup_test_environment(debug=self.debug_mode)

View File

@ -356,8 +356,11 @@ Tests
* Django test runner now supports a :option:`--buffer <test --buffer>` option * Django test runner now supports a :option:`--buffer <test --buffer>` option
with parallel tests. with parallel tests.
* The new :meth:`.DiscoverRunner.log` method allows customizing the way * The new ``logger`` argument to :class:`~django.test.runner.DiscoverRunner`
messages are logged. allows a Python :py:ref:`logger <logger>` to be used for logging.
* The new :meth:`.DiscoverRunner.log` method provides a way to log messages
that uses the ``DiscoverRunner.logger``, or prints to the console if not set.
* Django test runner now supports a :option:`--shuffle <test --shuffle>` option * Django test runner now supports a :option:`--shuffle <test --shuffle>` option
to execute tests in a random order. to execute tests in a random order.

View File

@ -510,7 +510,7 @@ behavior. This class defines the ``run_tests()`` entry point, plus a
selection of other methods that are used by ``run_tests()`` to set up, execute selection of other methods that are used by ``run_tests()`` to set up, execute
and tear down the test suite. and tear down the test suite.
.. class:: DiscoverRunner(pattern='test*.py', top_level=None, verbosity=1, interactive=True, failfast=False, keepdb=False, reverse=False, debug_mode=False, debug_sql=False, parallel=0, tags=None, exclude_tags=None, test_name_patterns=None, pdb=False, buffer=False, enable_faulthandler=True, timing=True, shuffle=False, **kwargs) .. class:: DiscoverRunner(pattern='test*.py', top_level=None, verbosity=1, interactive=True, failfast=False, keepdb=False, reverse=False, debug_mode=False, debug_sql=False, parallel=0, tags=None, exclude_tags=None, test_name_patterns=None, pdb=False, buffer=False, enable_faulthandler=True, timing=True, shuffle=False, logger=None, **kwargs)
``DiscoverRunner`` will search for tests in any file matching ``pattern``. ``DiscoverRunner`` will search for tests in any file matching ``pattern``.
@ -580,10 +580,15 @@ and tear down the test suite.
If ``shuffle`` is an integer, test cases will be shuffled in a random order If ``shuffle`` is an integer, test cases will be shuffled in a random order
prior to execution, using the integer as a random seed. If ``shuffle`` is prior to execution, using the integer as a random seed. If ``shuffle`` is
``None``, the seed will be generated randomly. In both cases, the seed will ``None``, the seed will be generated randomly. In both cases, the seed will
be logged to the console and set to ``self.shuffle_seed`` prior to running be logged and set to ``self.shuffle_seed`` prior to running tests. This
tests. This option can be used to help detect tests that aren't properly option can be used to help detect tests that aren't properly isolated.
isolated. :ref:`Grouping by test class <order-of-tests>` is preserved when :ref:`Grouping by test class <order-of-tests>` is preserved when using this
using this option. option.
``logger`` can be used to pass a Python :py:ref:`Logger object <logger>`.
If provided, the logger will be used to log messages instead of printing to
the console. The logger object will respect its logging level rather than
the ``verbosity``.
Django may, from time to time, extend the capabilities of the test runner Django may, from time to time, extend the capabilities of the test runner
by adding new arguments. The ``**kwargs`` declaration allows for this by adding new arguments. The ``**kwargs`` declaration allows for this
@ -601,7 +606,7 @@ and tear down the test suite.
.. versionadded:: 4.0 .. versionadded:: 4.0
The ``shuffle`` argument was added. The ``logger`` and ``shuffle`` arguments were added.
Attributes Attributes
~~~~~~~~~~ ~~~~~~~~~~
@ -726,11 +731,13 @@ Methods
.. versionadded:: 4.0 .. versionadded:: 4.0
Prints to the console a message with the given integer `logging level`_ If a ``logger`` is set, logs the message at the given integer
(e.g. ``logging.DEBUG``, ``logging.INFO``, or ``logging.WARNING``), `logging level`_ (e.g. ``logging.DEBUG``, ``logging.INFO``, or
respecting the current ``verbosity``. For example, an ``INFO`` message will ``logging.WARNING``). Otherwise, the message is printed to the console,
be logged if the ``verbosity`` is at least 1, and ``DEBUG`` will be logged respecting the current ``verbosity``. For example, no message will be
if it is at least 2. printed if the ``verbosity`` is 0, ``INFO`` and above will be printed if
the ``verbosity`` is at least 1, and ``DEBUG`` will be printed if it is at
least 2. The ``level`` defaults to ``logging.INFO``.
.. _`logging level`: https://docs.python.org/3/library/logging.html#levels .. _`logging level`: https://docs.python.org/3/library/logging.html#levels

View File

@ -623,6 +623,27 @@ class DiscoverRunnerTests(SimpleTestCase):
runner.log(msg, level) runner.log(msg, level)
self.assertEqual(stdout.getvalue(), f'{msg}\n' if output else '') self.assertEqual(stdout.getvalue(), f'{msg}\n' if output else '')
def test_log_logger(self):
logger = logging.getLogger('test.logging')
cases = [
(None, 'INFO:test.logging:log message'),
# Test a low custom logging level.
(5, 'Level 5:test.logging:log message'),
(logging.DEBUG, 'DEBUG:test.logging:log message'),
(logging.INFO, 'INFO:test.logging:log message'),
(logging.WARNING, 'WARNING:test.logging:log message'),
# Test a high custom logging level.
(45, 'Level 45:test.logging:log message'),
]
for level, expected in cases:
with self.subTest(level=level):
runner = DiscoverRunner(logger=logger)
# Pass a logging level smaller than the smallest level in cases
# in order to capture all messages.
with self.assertLogs('test.logging', level=1) as cm:
runner.log('log message', level)
self.assertEqual(cm.output, [expected])
class DiscoverRunnerGetDatabasesTests(SimpleTestCase): class DiscoverRunnerGetDatabasesTests(SimpleTestCase):
runner = DiscoverRunner(verbosity=2) runner = DiscoverRunner(verbosity=2)