aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/Lib/unittest
diff options
context:
space:
mode:
Diffstat (limited to 'Lib/unittest')
-rw-r--r--Lib/unittest/_log.py5
-rw-r--r--Lib/unittest/case.py10
-rw-r--r--Lib/unittest/mock.py15
-rw-r--r--Lib/unittest/suite.py20
4 files changed, 36 insertions, 14 deletions
diff --git a/Lib/unittest/_log.py b/Lib/unittest/_log.py
index 94868e5bb95..3d69385ea24 100644
--- a/Lib/unittest/_log.py
+++ b/Lib/unittest/_log.py
@@ -30,7 +30,7 @@ class _AssertLogsContext(_BaseTestCaseContext):
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)
self.logger_name = logger_name
if level:
@@ -39,13 +39,14 @@ class _AssertLogsContext(_BaseTestCaseContext):
self.level = logging.INFO
self.msg = None
self.no_logs = no_logs
+ self.formatter = formatter
def __enter__(self):
if isinstance(self.logger_name, logging.Logger):
logger = self.logger = self.logger_name
else:
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.setLevel(self.level)
handler.setFormatter(formatter)
diff --git a/Lib/unittest/case.py b/Lib/unittest/case.py
index 884fc1b21f6..eba50839cd3 100644
--- a/Lib/unittest/case.py
+++ b/Lib/unittest/case.py
@@ -149,9 +149,7 @@ def doModuleCleanups():
except Exception as exc:
exceptions.append(exc)
if exceptions:
- # Swallows all but first exception. If a multi-exception handler
- # gets written we should use that here instead.
- raise exceptions[0]
+ raise ExceptionGroup('module cleanup failed', exceptions)
def skip(reason):
@@ -851,7 +849,7 @@ class TestCase(object):
context = _AssertNotWarnsContext(expected_warning, self)
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
on *logger_name* or its children. If omitted, *level* defaults to
INFO and *logger* defaults to the root logger.
@@ -863,6 +861,8 @@ class TestCase(object):
`records` attribute will be a list of the corresponding LogRecord
objects.
+ Optionally supply `formatter` to control how messages are formatted.
+
Example::
with self.assertLogs('foo', level='INFO') as cm:
@@ -873,7 +873,7 @@ class TestCase(object):
"""
# Lazy import to avoid importing logging if it is not needed.
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):
""" Fail unless no log messages of level *level* or higher are emitted
diff --git a/Lib/unittest/mock.py b/Lib/unittest/mock.py
index 55cb4b1f6af..e1dbfdacf56 100644
--- a/Lib/unittest/mock.py
+++ b/Lib/unittest/mock.py
@@ -569,6 +569,11 @@ class NonCallableMock(Base):
__dict__['_mock_methods'] = spec
__dict__['_spec_asyncs'] = _spec_asyncs
+ def _mock_extend_spec_methods(self, spec_methods):
+ methods = self.__dict__.get('_mock_methods') or []
+ methods.extend(spec_methods)
+ self.__dict__['_mock_methods'] = methods
+
def __get_return_value(self):
ret = self._mock_return_value
if self._mock_delegate is not None:
@@ -981,7 +986,7 @@ class NonCallableMock(Base):
def assert_called_once_with(self, /, *args, **kwargs):
- """assert that the mock was called exactly once and that that call was
+ """assert that the mock was called exactly once and that call was
with the specified arguments."""
if not self.call_count == 1:
msg = ("Expected '%s' to be called once. Called %s times.%s"
@@ -2766,14 +2771,16 @@ def create_autospec(spec, spec_set=False, instance=False, _parent=None,
raise InvalidSpecError(f'Cannot autospec a Mock object. '
f'[object={spec!r}]')
is_async_func = _is_async_func(spec)
+ _kwargs = {'spec': spec}
entries = [(entry, _missing) for entry in dir(spec)]
if is_type and instance and is_dataclass(spec):
+ is_dataclass_spec = True
dataclass_fields = fields(spec)
entries.extend((f.name, f.type) for f in dataclass_fields)
- _kwargs = {'spec': [f.name for f in dataclass_fields]}
+ dataclass_spec_list = [f.name for f in dataclass_fields]
else:
- _kwargs = {'spec': spec}
+ is_dataclass_spec = False
if spec_set:
_kwargs = {'spec_set': spec}
@@ -2810,6 +2817,8 @@ def create_autospec(spec, spec_set=False, instance=False, _parent=None,
mock = Klass(parent=_parent, _new_parent=_parent, _new_name=_new_name,
name=_name, **_kwargs)
+ if is_dataclass_spec:
+ mock._mock_extend_spec_methods(dataclass_spec_list)
if isinstance(spec, FunctionTypes):
# should only happen at the top level because we don't
diff --git a/Lib/unittest/suite.py b/Lib/unittest/suite.py
index 6f45b6fe5f6..ae9ca2d615d 100644
--- a/Lib/unittest/suite.py
+++ b/Lib/unittest/suite.py
@@ -223,6 +223,11 @@ class TestSuite(BaseTestSuite):
if result._moduleSetUpFailed:
try:
case.doModuleCleanups()
+ except ExceptionGroup as eg:
+ for e in eg.exceptions:
+ self._createClassOrModuleLevelException(result, e,
+ 'setUpModule',
+ currentModule)
except Exception as e:
self._createClassOrModuleLevelException(result, e,
'setUpModule',
@@ -235,15 +240,15 @@ class TestSuite(BaseTestSuite):
errorName = f'{method_name} ({parent})'
self._addClassOrModuleLevelException(result, exc, errorName, info)
- def _addClassOrModuleLevelException(self, result, exception, errorName,
+ def _addClassOrModuleLevelException(self, result, exc, errorName,
info=None):
error = _ErrorHolder(errorName)
addSkip = getattr(result, 'addSkip', None)
- if addSkip is not None and isinstance(exception, case.SkipTest):
- addSkip(error, str(exception))
+ if addSkip is not None and isinstance(exc, case.SkipTest):
+ addSkip(error, str(exc))
else:
if not info:
- result.addError(error, sys.exc_info())
+ result.addError(error, (type(exc), exc, exc.__traceback__))
else:
result.addError(error, info)
@@ -273,6 +278,13 @@ class TestSuite(BaseTestSuite):
previousModule)
try:
case.doModuleCleanups()
+ except ExceptionGroup as eg:
+ if isinstance(result, _DebugResult):
+ raise
+ for e in eg.exceptions:
+ self._createClassOrModuleLevelException(result, e,
+ 'tearDownModule',
+ previousModule)
except Exception as e:
if isinstance(result, _DebugResult):
raise