Skip to main content

Lib/unittest/case.py (part 3)

Source:

cpython 3.14 @ ab2d84fe1023/Lib/unittest/case.py

This annotation covers assertion helpers and test control flow. See lib_unittest2_detail for TestCase.run, setUp/tearDown, and the result protocol.

Map

LinesSymbolRole
1-80assertEqualCore equality check with type-specific helpers
81-160assertRaisesVerify a callable raises a specific exception
161-240assertWarnsVerify a callable issues a specific warning
241-320subTestContext manager for parameterized sub-cases
321-500Skip decorators@skip, @skipIf, @skipUnless, @expectedFailure

Reading

assertEqual

# CPython: Lib/unittest/case.py:870 TestCase.assertEqual
def assertEqual(self, first, second, msg=None):
if not first == second:
firstclass = first.__class__
secondclass = second.__class__
if firstclass is not secondclass:
assertion_func = self._baseAssertEqual
else:
assertion_func = self._type_equality_funcs.get(
firstclass, self._baseAssertEqual)
assertion_func(first, second, msg=msg)

_type_equality_funcs maps types to specialized comparers: assertMultiLineEqual for str, assertSequenceEqual for list/tuple, assertDictEqual for dict, assertSetEqual for set. These produce more informative diffs than the generic message.

assertRaises

# CPython: Lib/unittest/case.py:740 TestCase.assertRaises
def assertRaises(self, expected_exception, *args, **kwargs):
context = _AssertRaisesContext(expected_exception, self)
try:
return context.handle('assertRaises', args, kwargs)
except:
...

class _AssertRaisesContext:
def __exit__(self, exc_type, exc_val, exc_tb):
if exc_type is None:
raise self.test_case.failureException(
f'{self.expected.__name__} not raised')
if not issubclass(exc_type, self.expected):
return False # re-raise
self.exception = exc_val
return True

Used as with self.assertRaises(ValueError): or self.assertRaises(ValueError, fn, arg). As a context manager, it suppresses the expected exception and stores it in .exception. Unexpected exceptions propagate.

subTest

# CPython: Lib/unittest/case.py:1100 TestCase.subTest
@contextmanager
def subTest(self, msg=_subtest_msg_sentinel, **params):
if not isinstance(self._outcome, _Outcome):
yield
return
parent = self._subtest
if parent is None:
self._subtest = _SubTest(self, msg, params)
else:
self._subtest = _SubTest(self, msg, {**parent.params, **params})
try:
with self._outcome.testPartExecutor(self._subtest):
yield
finally:
self._subtest = parent

subTest allows a loop to run multiple sub-cases without stopping on the first failure. Each failure is recorded independently; the test continues. Parameters appear in the failure message: FAIL: test_foo (msg='bar', i=3).

Skip decorators

# CPython: Lib/unittest/case.py:180 skip
def skip(reason):
def decorator(test_item):
if not isinstance(test_item, type):
@functools.wraps(test_item)
def skip_wrapper(*args, **kwargs):
raise SkipTest(reason)
test_item = skip_wrapper
test_item.__unittest_skip__ = True
test_item.__unittest_skip_why__ = reason
return test_item
return decorator

@skip("reason") works on both test methods and entire TestCase classes. For classes, the __unittest_skip__ attribute is checked by the loader. @expectedFailure marks the test as expected to fail; an unexpected pass is reported as an error.

gopy notes

TestCase and its assertion helpers are in module/unittest/module.go. assertEqual dispatches to type-specific comparers via a Go map. assertRaises is implemented as a Go context object. subTest uses a nested result collector. Skip decorators set metadata fields on the callable wrapper.