Skip to main content

Lib/unittest/loader.py

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

Lib/unittest/loader.py implements TestLoader, the class responsible for building TestSuite objects from test sources. It can load tests from a TestCase class, a module, a dotted name string, or walk a directory tree using the discovery protocol. The module-level defaultTestLoader instance is what python -m unittest discover uses.

Map

LinesSymbolRole
1-40imports, VALID_MODULE_NAMERegex for test*.py pattern matching
41-120TestLoader attributestestMethodPrefix, sortTestMethodsUsing, suiteClass
121-220loadTestsFromTestCaseLoad all test* methods from a TestCase subclass
221-310loadTestsFromModuleInspect module for TestCase subclasses
311-380loadTestsFromName, loadTestsFromNamesDotted-name resolution and batch loading
381-480discoverDirectory walk with VALID_MODULE_NAME pattern
481-520defaultTestLoaderModule-level singleton instance

Reading

loadTestsFromTestCase: method name collection

loadTestsFromTestCase calls getTestCaseNames to collect all methods whose names start with testMethodPrefix (default "test"), then sorts them using sortTestMethodsUsing (default str.__lt__). Each name becomes a TestCase instance with that method as its runTest.

# CPython: Lib/unittest/loader.py:130 loadTestsFromTestCase
def loadTestsFromTestCase(self, testCaseClass):
if issubclass(testCaseClass, suite.TestSuite):
raise TypeError(...)
testCaseNames = self.getTestCaseNames(testCaseClass)
if not testCaseNames and hasattr(testCaseClass, 'runTest'):
testCaseNames = ['runTest']
loaded_suite = self.suiteClass(map(testCaseClass, testCaseNames))
return loaded_suite

loadTestsFromName: dotted-name resolution

loadTestsFromName splits a dotted name and resolves it step by step: first importing the longest importable prefix, then using getattr for the remaining parts. The result can be a module, class, method, or TestSuite.

# CPython: Lib/unittest/loader.py:250 loadTestsFromName
def loadTestsFromName(self, name, module=None):
parts = name.split('.')
if module is None:
parts_copy = parts[:]
while parts_copy:
try:
module = __import__('.'.join(parts_copy))
break
except ImportError:
del parts_copy[-1]

discover: directory walk and pattern matching

discover walks the start directory recursively looking for files whose names match VALID_MODULE_NAME (default test*.py). Each matching file is imported as a module using its dotted path relative to the top-level directory, and loadTestsFromModule is called on the result.

# CPython: Lib/unittest/loader.py:400 _get_module_from_path
def _get_module_from_path(self, path, full_path, pattern):
...
name = self._get_name_from_path(full_path)
try:
module = sys.modules[name]
except KeyError:
...
module = importlib.import_module(name)

gopy notes

Not yet ported. gopy's test suite uses Go's testing package and go test. The TestLoader discovery protocol is only relevant when running Lib/test/ against gopy's VM, at which point the standard unittest discovery mechanism drives test collection.

CPython 3.14 changes

3.14 added TestLoader.errors as a list that accumulates loading errors rather than raising immediately, allowing discovery to continue past broken modules. The VALID_MODULE_NAME regex is now exposed as a class attribute that subclasses can override.