mirror of
https://github.com/python/cpython.git
synced 2025-07-07 19:35:27 +00:00
gh-134567: Add the formatter parameter in unittest.TestCase.assertLogs (GH-134570)
This commit is contained in:
parent
b19c9da401
commit
51ab66b3d5
6 changed files with 42 additions and 5 deletions
|
@ -1131,7 +1131,7 @@ Test cases
|
||||||
.. versionchanged:: 3.3
|
.. versionchanged:: 3.3
|
||||||
Added the *msg* keyword argument when used as a context manager.
|
Added the *msg* keyword argument when used as a context manager.
|
||||||
|
|
||||||
.. method:: assertLogs(logger=None, level=None)
|
.. method:: assertLogs(logger=None, level=None, formatter=None)
|
||||||
|
|
||||||
A context manager to test that at least one message is logged on
|
A context manager to test that at least one message is logged on
|
||||||
the *logger* or one of its children, with at least the given
|
the *logger* or one of its children, with at least the given
|
||||||
|
@ -1146,6 +1146,10 @@ Test cases
|
||||||
its string equivalent (for example either ``"ERROR"`` or
|
its string equivalent (for example either ``"ERROR"`` or
|
||||||
:const:`logging.ERROR`). The default is :const:`logging.INFO`.
|
:const:`logging.ERROR`). The default is :const:`logging.INFO`.
|
||||||
|
|
||||||
|
If given, *formatter* should be a :class:`logging.Formatter` object.
|
||||||
|
The default is a formatter with format string
|
||||||
|
``"%(levelname)s:%(name)s:%(message)s"``
|
||||||
|
|
||||||
The test passes if at least one message emitted inside the ``with``
|
The test passes if at least one message emitted inside the ``with``
|
||||||
block matches the *logger* and *level* conditions, otherwise it fails.
|
block matches the *logger* and *level* conditions, otherwise it fails.
|
||||||
|
|
||||||
|
@ -1173,6 +1177,9 @@ Test cases
|
||||||
|
|
||||||
.. versionadded:: 3.4
|
.. versionadded:: 3.4
|
||||||
|
|
||||||
|
.. versionchanged:: next
|
||||||
|
Now accepts a *formatter* to control how messages are formatted.
|
||||||
|
|
||||||
.. method:: assertNoLogs(logger=None, level=None)
|
.. method:: assertNoLogs(logger=None, level=None)
|
||||||
|
|
||||||
A context manager to test that no messages are logged on
|
A context manager to test that no messages are logged on
|
||||||
|
|
|
@ -291,6 +291,15 @@ typing
|
||||||
(Contributed by Bénédikt Tran in :gh:`133823`.)
|
(Contributed by Bénédikt Tran in :gh:`133823`.)
|
||||||
|
|
||||||
|
|
||||||
|
unittest
|
||||||
|
--------
|
||||||
|
|
||||||
|
* Lets users specify formatter in TestCase.assertLogs.
|
||||||
|
:func:`unittest.TestCase.assertLogs` will now accept a formatter
|
||||||
|
to control how messages are formatted.
|
||||||
|
(Contributed by Garry Cairns in :gh:`134567`.)
|
||||||
|
|
||||||
|
|
||||||
wave
|
wave
|
||||||
----
|
----
|
||||||
|
|
||||||
|
|
|
@ -1920,6 +1920,22 @@ test case
|
||||||
with self.assertLogs():
|
with self.assertLogs():
|
||||||
raise ZeroDivisionError("Unexpected")
|
raise ZeroDivisionError("Unexpected")
|
||||||
|
|
||||||
|
def testAssertLogsWithFormatter(self):
|
||||||
|
# Check alternative formats will be respected
|
||||||
|
format = "[No.1: the larch] %(levelname)s:%(name)s:%(message)s"
|
||||||
|
formatter = logging.Formatter(format)
|
||||||
|
with self.assertNoStderr():
|
||||||
|
with self.assertLogs() as cm:
|
||||||
|
log_foo.info("1")
|
||||||
|
log_foobar.debug("2")
|
||||||
|
self.assertEqual(cm.output, ["INFO:foo:1"])
|
||||||
|
self.assertLogRecords(cm.records, [{'name': 'foo'}])
|
||||||
|
with self.assertLogs(formatter=formatter) as cm:
|
||||||
|
log_foo.info("1")
|
||||||
|
log_foobar.debug("2")
|
||||||
|
self.assertEqual(cm.output, ["[No.1: the larch] INFO:foo:1"])
|
||||||
|
self.assertLogRecords(cm.records, [{'name': 'foo'}])
|
||||||
|
|
||||||
def testAssertNoLogsDefault(self):
|
def testAssertNoLogsDefault(self):
|
||||||
with self.assertRaises(self.failureException) as cm:
|
with self.assertRaises(self.failureException) as cm:
|
||||||
with self.assertNoLogs():
|
with self.assertNoLogs():
|
||||||
|
|
|
@ -30,7 +30,7 @@ class _AssertLogsContext(_BaseTestCaseContext):
|
||||||
|
|
||||||
LOGGING_FORMAT = "%(levelname)s:%(name)s:%(message)s"
|
LOGGING_FORMAT = "%(levelname)s:%(name)s:%(message)s"
|
||||||
|
|
||||||
def __init__(self, test_case, logger_name, level, no_logs):
|
def __init__(self, test_case, logger_name, level, no_logs, formatter=None):
|
||||||
_BaseTestCaseContext.__init__(self, test_case)
|
_BaseTestCaseContext.__init__(self, test_case)
|
||||||
self.logger_name = logger_name
|
self.logger_name = logger_name
|
||||||
if level:
|
if level:
|
||||||
|
@ -39,13 +39,14 @@ class _AssertLogsContext(_BaseTestCaseContext):
|
||||||
self.level = logging.INFO
|
self.level = logging.INFO
|
||||||
self.msg = None
|
self.msg = None
|
||||||
self.no_logs = no_logs
|
self.no_logs = no_logs
|
||||||
|
self.formatter = formatter
|
||||||
|
|
||||||
def __enter__(self):
|
def __enter__(self):
|
||||||
if isinstance(self.logger_name, logging.Logger):
|
if isinstance(self.logger_name, logging.Logger):
|
||||||
logger = self.logger = self.logger_name
|
logger = self.logger = self.logger_name
|
||||||
else:
|
else:
|
||||||
logger = self.logger = logging.getLogger(self.logger_name)
|
logger = self.logger = logging.getLogger(self.logger_name)
|
||||||
formatter = logging.Formatter(self.LOGGING_FORMAT)
|
formatter = self.formatter or logging.Formatter(self.LOGGING_FORMAT)
|
||||||
handler = _CapturingHandler()
|
handler = _CapturingHandler()
|
||||||
handler.setLevel(self.level)
|
handler.setLevel(self.level)
|
||||||
handler.setFormatter(formatter)
|
handler.setFormatter(formatter)
|
||||||
|
|
|
@ -849,7 +849,7 @@ class TestCase(object):
|
||||||
context = _AssertNotWarnsContext(expected_warning, self)
|
context = _AssertNotWarnsContext(expected_warning, self)
|
||||||
return context.handle('_assertNotWarns', args, kwargs)
|
return context.handle('_assertNotWarns', args, kwargs)
|
||||||
|
|
||||||
def assertLogs(self, logger=None, level=None):
|
def assertLogs(self, logger=None, level=None, formatter=None):
|
||||||
"""Fail unless a log message of level *level* or higher is emitted
|
"""Fail unless a log message of level *level* or higher is emitted
|
||||||
on *logger_name* or its children. If omitted, *level* defaults to
|
on *logger_name* or its children. If omitted, *level* defaults to
|
||||||
INFO and *logger* defaults to the root logger.
|
INFO and *logger* defaults to the root logger.
|
||||||
|
@ -861,6 +861,8 @@ class TestCase(object):
|
||||||
`records` attribute will be a list of the corresponding LogRecord
|
`records` attribute will be a list of the corresponding LogRecord
|
||||||
objects.
|
objects.
|
||||||
|
|
||||||
|
Optionally supply `formatter` to control how messages are formatted.
|
||||||
|
|
||||||
Example::
|
Example::
|
||||||
|
|
||||||
with self.assertLogs('foo', level='INFO') as cm:
|
with self.assertLogs('foo', level='INFO') as cm:
|
||||||
|
@ -871,7 +873,7 @@ class TestCase(object):
|
||||||
"""
|
"""
|
||||||
# Lazy import to avoid importing logging if it is not needed.
|
# Lazy import to avoid importing logging if it is not needed.
|
||||||
from ._log import _AssertLogsContext
|
from ._log import _AssertLogsContext
|
||||||
return _AssertLogsContext(self, logger, level, no_logs=False)
|
return _AssertLogsContext(self, logger, level, no_logs=False, formatter=formatter)
|
||||||
|
|
||||||
def assertNoLogs(self, logger=None, level=None):
|
def assertNoLogs(self, logger=None, level=None):
|
||||||
""" Fail unless no log messages of level *level* or higher are emitted
|
""" Fail unless no log messages of level *level* or higher are emitted
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
Expose log formatter to users in TestCase.assertLogs.
|
||||||
|
:func:`unittest.TestCase.assertLogs` will now optionally accept a formatter that will be used to format the strings in output if provided.
|
Loading…
Add table
Add a link
Reference in a new issue