"""Unittest main program"""
from . import loader, runner
from .signals import installHandler
%(prog)s test_module - run tests from test_module
%(prog)s module.TestClass - run tests from module.TestClass
%(prog)s module.Class.test_method - run specified test method
%(prog)s path/to/test_file.py - run tests from test_file.py
%(prog)s - run default set of tests
%(prog)s MyTestSuite - run suite 'MyTestSuite'
%(prog)s MyTestCase.testSomething - run MyTestCase.testSomething
%(prog)s MyTestCase - run all 'test*' test methods
# on Linux / Mac OS X 'foo.PY' is not importable, but on
# Windows it is. Simpler to do a case insensitive match
# a better check would be to check that the name is a
# valid Python module name.
if os.path.isfile(name) and name.lower().endswith('.py'):
rel_path = os.path.relpath(name, os.getcwd())
if os.path.isabs(rel_path) or rel_path.startswith(os.pardir):
# on Windows both '\' and '/' are used as path
# separators. Better to replace both than rely on os.path.sep
return name[:-3].replace('\\', '.').replace('/', '.')
def _convert_names(names):
return [_convert_name(name) for name in names]
def _convert_select_pattern(pattern):
pattern = '*%s*' % pattern
class TestProgram(object):
"""A command-line program that runs a set of tests; this is primarily
for making test modules conveniently executable.
failfast = catchbreak = buffer = progName = warnings = testNamePatterns = None
def __init__(self, module='__main__', defaultTest=None, argv=None,
testRunner=None, testLoader=loader.defaultTestLoader,
exit=True, verbosity=1, failfast=None, catchbreak=None,
buffer=None, warnings=None, *, tb_locals=False):
if isinstance(module, str):
self.module = __import__(module)
for part in module.split('.')[1:]:
self.module = getattr(self.module, part)
self.catchbreak = catchbreak
self.verbosity = verbosity
self.tb_locals = tb_locals
if warnings is None and not sys.warnoptions:
# even if DeprecationWarnings are ignored by default
# print them anyway unless other warnings settings are
# specified by the warnings arg or the -W python flag
self.warnings = 'default'
# here self.warnings is set either to the value passed
# to the warnings args or to None.
# If the user didn't pass a value self.warnings will
# be None. This means that the behavior is unchanged
# and depends on the values passed to -W.
self.defaultTest = defaultTest
self.testRunner = testRunner
self.testLoader = testLoader
self.progName = os.path.basename(argv[0])
def usageExit(self, msg=None):
if self._discovery_parser is None:
def _print_help(self, *args, **kwargs):
print(self._main_parser.format_help())
print(MAIN_EXAMPLES % {'prog': self.progName})
self._discovery_parser.print_help()
print(self._main_parser.format_help())
print(MODULE_EXAMPLES % {'prog': self.progName})
def parseArgs(self, argv):
if len(argv) > 1 and argv[1].lower() == 'discover':
self._do_discovery(argv[2:])
self._main_parser.parse_args(argv[1:], self)
# this allows "python -m unittest -v" to still work for
self._main_parser.parse_args(argv[1:], self)
self.testNames = _convert_names(self.tests)
if __name__ == '__main__':
# to support python -m unittest ...
elif self.defaultTest is None:
# createTests will load tests from self.module
elif isinstance(self.defaultTest, str):
self.testNames = (self.defaultTest,)
self.testNames = list(self.defaultTest)
def createTests(self, from_discovery=False, Loader=None):
if self.testNamePatterns:
self.testLoader.testNamePatterns = self.testNamePatterns
loader = self.testLoader if Loader is None else Loader()
self.test = loader.discover(self.start, self.pattern, self.top)
elif self.testNames is None:
self.test = self.testLoader.loadTestsFromModule(self.module)
self.test = self.testLoader.loadTestsFromNames(self.testNames,
def _initArgParsers(self):
parent_parser = self._getParentArgParser()
self._main_parser = self._getMainArgParser(parent_parser)
self._discovery_parser = self._getDiscoveryArgParser(parent_parser)
def _getParentArgParser(self):
parser = argparse.ArgumentParser(add_help=False)
parser.add_argument('-v', '--verbose', dest='verbosity',
action='store_const', const=2,
parser.add_argument('-q', '--quiet', dest='verbosity',
action='store_const', const=0,
parser.add_argument('--locals', dest='tb_locals',
help='Show local variables in tracebacks')
if self.failfast is None:
parser.add_argument('-f', '--failfast', dest='failfast',
help='Stop on first fail or error')
if self.catchbreak is None:
parser.add_argument('-c', '--catch', dest='catchbreak',
help='Catch Ctrl-C and display results so far')
parser.add_argument('-b', '--buffer', dest='buffer',
help='Buffer stdout and stderr during tests')
if self.testNamePatterns is None:
parser.add_argument('-k', dest='testNamePatterns',
action='append', type=_convert_select_pattern,
help='Only run tests which match the given substring')
self.testNamePatterns = []
def _getMainArgParser(self, parent):
parser = argparse.ArgumentParser(parents=[parent])
parser.prog = self.progName
parser.print_help = self._print_help
parser.add_argument('tests', nargs='*',
help='a list of any number of test modules, '
'classes and test methods.')
def _getDiscoveryArgParser(self, parent):
parser = argparse.ArgumentParser(parents=[parent])
parser.prog = '%s discover' % self.progName
parser.epilog = ('For test discovery all test modules must be '
'importable from the top level directory of the '
parser.add_argument('-s', '--start-directory', dest='start',
help="Directory to start discovery ('.' default)")
parser.add_argument('-p', '--pattern', dest='pattern',
help="Pattern to match tests ('test*.py' default)")
parser.add_argument('-t', '--top-level-directory', dest='top',
help='Top level directory of project (defaults to '
for arg in ('start', 'pattern', 'top'):
parser.add_argument(arg, nargs='?',
default=argparse.SUPPRESS,
def _do_discovery(self, argv, Loader=None):
self.pattern = 'test*.py'
# handle command line args for test discovery
if self._discovery_parser is None:
self._discovery_parser.parse_args(argv, self)
self.createTests(from_discovery=True, Loader=Loader)
if self.testRunner is None:
self.testRunner = runner.TextTestRunner
if isinstance(self.testRunner, type):
testRunner = self.testRunner(verbosity=self.verbosity,
tb_locals=self.tb_locals)
# didn't accept the tb_locals argument
testRunner = self.testRunner(verbosity=self.verbosity,
# didn't accept the verbosity, buffer or failfast arguments
testRunner = self.testRunner()
# it is assumed to be a TestRunner instance
testRunner = self.testRunner
self.result = testRunner.run(self.test)
sys.exit(not self.result.wasSuccessful())