unittest.main
Unittest main program
1"""Unittest main program""" 2 3import sys 4import argparse 5import os 6import warnings 7 8from . import loader, runner 9from .signals import installHandler 10 11__unittest = True 12_NO_TESTS_EXITCODE = 5 13 14MAIN_EXAMPLES = """\ 15Examples: 16 %(prog)s test_module - run tests from test_module 17 %(prog)s module.TestClass - run tests from module.TestClass 18 %(prog)s module.Class.test_method - run specified test method 19 %(prog)s path/to/test_file.py - run tests from test_file.py 20""" 21 22MODULE_EXAMPLES = """\ 23Examples: 24 %(prog)s - run default set of tests 25 %(prog)s MyTestSuite - run suite 'MyTestSuite' 26 %(prog)s MyTestCase.testSomething - run MyTestCase.testSomething 27 %(prog)s MyTestCase - run all 'test*' test methods 28 in MyTestCase 29""" 30 31def _convert_name(name): 32 # on Linux / Mac OS X 'foo.PY' is not importable, but on 33 # Windows it is. Simpler to do a case insensitive match 34 # a better check would be to check that the name is a 35 # valid Python module name. 36 if os.path.isfile(name) and name.lower().endswith('.py'): 37 if os.path.isabs(name): 38 rel_path = os.path.relpath(name, os.getcwd()) 39 if os.path.isabs(rel_path) or rel_path.startswith(os.pardir): 40 return name 41 name = rel_path 42 # on Windows both '\' and '/' are used as path 43 # separators. Better to replace both than rely on os.path.sep 44 return os.path.normpath(name)[:-3].replace('\\', '.').replace('/', '.') 45 return name 46 47def _convert_names(names): 48 return [_convert_name(name) for name in names] 49 50 51def _convert_select_pattern(pattern): 52 if not '*' in pattern: 53 pattern = '*%s*' % pattern 54 return pattern 55 56 57class TestProgram(object): 58 """A command-line program that runs a set of tests; this is primarily 59 for making test modules conveniently executable. 60 """ 61 # defaults for testing 62 module=None 63 verbosity = 1 64 failfast = catchbreak = buffer = progName = warnings = testNamePatterns = None 65 _discovery_parser = None 66 67 def __init__(self, module='__main__', defaultTest=None, argv=None, 68 testRunner=None, testLoader=loader.defaultTestLoader, 69 exit=True, verbosity=1, failfast=None, catchbreak=None, 70 buffer=None, warnings=None, *, tb_locals=False, 71 durations=None): 72 if isinstance(module, str): 73 self.module = __import__(module) 74 for part in module.split('.')[1:]: 75 self.module = getattr(self.module, part) 76 else: 77 self.module = module 78 if argv is None: 79 argv = sys.argv 80 81 self.exit = exit 82 self.failfast = failfast 83 self.catchbreak = catchbreak 84 self.verbosity = verbosity 85 self.buffer = buffer 86 self.tb_locals = tb_locals 87 self.durations = durations 88 if warnings is None and not sys.warnoptions: 89 # even if DeprecationWarnings are ignored by default 90 # print them anyway unless other warnings settings are 91 # specified by the warnings arg or the -W python flag 92 self.warnings = 'default' 93 else: 94 # here self.warnings is set either to the value passed 95 # to the warnings args or to None. 96 # If the user didn't pass a value self.warnings will 97 # be None. This means that the behavior is unchanged 98 # and depends on the values passed to -W. 99 self.warnings = warnings 100 self.defaultTest = defaultTest 101 self.testRunner = testRunner 102 self.testLoader = testLoader 103 self.progName = os.path.basename(argv[0]) 104 self.parseArgs(argv) 105 self.runTests() 106 107 def usageExit(self, msg=None): 108 warnings.warn("TestProgram.usageExit() is deprecated and will be" 109 " removed in Python 3.13", DeprecationWarning) 110 if msg: 111 print(msg) 112 if self._discovery_parser is None: 113 self._initArgParsers() 114 self._print_help() 115 sys.exit(2) 116 117 def _print_help(self, *args, **kwargs): 118 if self.module is None: 119 print(self._main_parser.format_help()) 120 print(MAIN_EXAMPLES % {'prog': self.progName}) 121 self._discovery_parser.print_help() 122 else: 123 print(self._main_parser.format_help()) 124 print(MODULE_EXAMPLES % {'prog': self.progName}) 125 126 def parseArgs(self, argv): 127 self._initArgParsers() 128 if self.module is None: 129 if len(argv) > 1 and argv[1].lower() == 'discover': 130 self._do_discovery(argv[2:]) 131 return 132 self._main_parser.parse_args(argv[1:], self) 133 if not self.tests: 134 # this allows "python -m unittest -v" to still work for 135 # test discovery. 136 self._do_discovery([]) 137 return 138 else: 139 self._main_parser.parse_args(argv[1:], self) 140 141 if self.tests: 142 self.testNames = _convert_names(self.tests) 143 if __name__ == '__main__': 144 # to support python -m unittest ... 145 self.module = None 146 elif self.defaultTest is None: 147 # createTests will load tests from self.module 148 self.testNames = None 149 elif isinstance(self.defaultTest, str): 150 self.testNames = (self.defaultTest,) 151 else: 152 self.testNames = list(self.defaultTest) 153 self.createTests() 154 155 def createTests(self, from_discovery=False, Loader=None): 156 if self.testNamePatterns: 157 self.testLoader.testNamePatterns = self.testNamePatterns 158 if from_discovery: 159 loader = self.testLoader if Loader is None else Loader() 160 self.test = loader.discover(self.start, self.pattern, self.top) 161 elif self.testNames is None: 162 self.test = self.testLoader.loadTestsFromModule(self.module) 163 else: 164 self.test = self.testLoader.loadTestsFromNames(self.testNames, 165 self.module) 166 167 def _initArgParsers(self): 168 parent_parser = self._getParentArgParser() 169 self._main_parser = self._getMainArgParser(parent_parser) 170 self._discovery_parser = self._getDiscoveryArgParser(parent_parser) 171 172 def _getParentArgParser(self): 173 parser = argparse.ArgumentParser(add_help=False) 174 175 parser.add_argument('-v', '--verbose', dest='verbosity', 176 action='store_const', const=2, 177 help='Verbose output') 178 parser.add_argument('-q', '--quiet', dest='verbosity', 179 action='store_const', const=0, 180 help='Quiet output') 181 parser.add_argument('--locals', dest='tb_locals', 182 action='store_true', 183 help='Show local variables in tracebacks') 184 parser.add_argument('--durations', dest='durations', type=int, 185 default=None, metavar="N", 186 help='Show the N slowest test cases (N=0 for all)') 187 if self.failfast is None: 188 parser.add_argument('-f', '--failfast', dest='failfast', 189 action='store_true', 190 help='Stop on first fail or error') 191 self.failfast = False 192 if self.catchbreak is None: 193 parser.add_argument('-c', '--catch', dest='catchbreak', 194 action='store_true', 195 help='Catch Ctrl-C and display results so far') 196 self.catchbreak = False 197 if self.buffer is None: 198 parser.add_argument('-b', '--buffer', dest='buffer', 199 action='store_true', 200 help='Buffer stdout and stderr during tests') 201 self.buffer = False 202 if self.testNamePatterns is None: 203 parser.add_argument('-k', dest='testNamePatterns', 204 action='append', type=_convert_select_pattern, 205 help='Only run tests which match the given substring') 206 self.testNamePatterns = [] 207 208 return parser 209 210 def _getMainArgParser(self, parent): 211 parser = argparse.ArgumentParser(parents=[parent]) 212 parser.prog = self.progName 213 parser.print_help = self._print_help 214 215 parser.add_argument('tests', nargs='*', 216 help='a list of any number of test modules, ' 217 'classes and test methods.') 218 219 return parser 220 221 def _getDiscoveryArgParser(self, parent): 222 parser = argparse.ArgumentParser(parents=[parent]) 223 parser.prog = '%s discover' % self.progName 224 parser.epilog = ('For test discovery all test modules must be ' 225 'importable from the top level directory of the ' 226 'project.') 227 228 parser.add_argument('-s', '--start-directory', dest='start', 229 help="Directory to start discovery ('.' default)") 230 parser.add_argument('-p', '--pattern', dest='pattern', 231 help="Pattern to match tests ('test*.py' default)") 232 parser.add_argument('-t', '--top-level-directory', dest='top', 233 help='Top level directory of project (defaults to ' 234 'start directory)') 235 for arg in ('start', 'pattern', 'top'): 236 parser.add_argument(arg, nargs='?', 237 default=argparse.SUPPRESS, 238 help=argparse.SUPPRESS) 239 240 return parser 241 242 def _do_discovery(self, argv, Loader=None): 243 self.start = '.' 244 self.pattern = 'test*.py' 245 self.top = None 246 if argv is not None: 247 # handle command line args for test discovery 248 if self._discovery_parser is None: 249 # for testing 250 self._initArgParsers() 251 self._discovery_parser.parse_args(argv, self) 252 253 self.createTests(from_discovery=True, Loader=Loader) 254 255 def runTests(self): 256 if self.catchbreak: 257 installHandler() 258 if self.testRunner is None: 259 self.testRunner = runner.TextTestRunner 260 if isinstance(self.testRunner, type): 261 try: 262 try: 263 testRunner = self.testRunner(verbosity=self.verbosity, 264 failfast=self.failfast, 265 buffer=self.buffer, 266 warnings=self.warnings, 267 tb_locals=self.tb_locals, 268 durations=self.durations) 269 except TypeError: 270 # didn't accept the tb_locals or durations argument 271 testRunner = self.testRunner(verbosity=self.verbosity, 272 failfast=self.failfast, 273 buffer=self.buffer, 274 warnings=self.warnings) 275 except TypeError: 276 # didn't accept the verbosity, buffer or failfast arguments 277 testRunner = self.testRunner() 278 else: 279 # it is assumed to be a TestRunner instance 280 testRunner = self.testRunner 281 self.result = testRunner.run(self.test) 282 if self.exit: 283 if self.result.testsRun == 0: 284 sys.exit(_NO_TESTS_EXITCODE) 285 elif self.result.wasSuccessful(): 286 sys.exit(0) 287 else: 288 sys.exit(1) 289 290 291main = TestProgram
MAIN_EXAMPLES =
'Examples:\n %(prog)s test_module - run tests from test_module\n %(prog)s module.TestClass - run tests from module.TestClass\n %(prog)s module.Class.test_method - run specified test method\n %(prog)s path/to/test_file.py - run tests from test_file.py\n'
MODULE_EXAMPLES =
"Examples:\n %(prog)s - run default set of tests\n %(prog)s MyTestSuite - run suite 'MyTestSuite'\n %(prog)s MyTestCase.testSomething - run MyTestCase.testSomething\n %(prog)s MyTestCase - run all 'test*' test methods\n in MyTestCase\n"
class
TestProgram:
58class TestProgram(object): 59 """A command-line program that runs a set of tests; this is primarily 60 for making test modules conveniently executable. 61 """ 62 # defaults for testing 63 module=None 64 verbosity = 1 65 failfast = catchbreak = buffer = progName = warnings = testNamePatterns = None 66 _discovery_parser = None 67 68 def __init__(self, module='__main__', defaultTest=None, argv=None, 69 testRunner=None, testLoader=loader.defaultTestLoader, 70 exit=True, verbosity=1, failfast=None, catchbreak=None, 71 buffer=None, warnings=None, *, tb_locals=False, 72 durations=None): 73 if isinstance(module, str): 74 self.module = __import__(module) 75 for part in module.split('.')[1:]: 76 self.module = getattr(self.module, part) 77 else: 78 self.module = module 79 if argv is None: 80 argv = sys.argv 81 82 self.exit = exit 83 self.failfast = failfast 84 self.catchbreak = catchbreak 85 self.verbosity = verbosity 86 self.buffer = buffer 87 self.tb_locals = tb_locals 88 self.durations = durations 89 if warnings is None and not sys.warnoptions: 90 # even if DeprecationWarnings are ignored by default 91 # print them anyway unless other warnings settings are 92 # specified by the warnings arg or the -W python flag 93 self.warnings = 'default' 94 else: 95 # here self.warnings is set either to the value passed 96 # to the warnings args or to None. 97 # If the user didn't pass a value self.warnings will 98 # be None. This means that the behavior is unchanged 99 # and depends on the values passed to -W. 100 self.warnings = warnings 101 self.defaultTest = defaultTest 102 self.testRunner = testRunner 103 self.testLoader = testLoader 104 self.progName = os.path.basename(argv[0]) 105 self.parseArgs(argv) 106 self.runTests() 107 108 def usageExit(self, msg=None): 109 warnings.warn("TestProgram.usageExit() is deprecated and will be" 110 " removed in Python 3.13", DeprecationWarning) 111 if msg: 112 print(msg) 113 if self._discovery_parser is None: 114 self._initArgParsers() 115 self._print_help() 116 sys.exit(2) 117 118 def _print_help(self, *args, **kwargs): 119 if self.module is None: 120 print(self._main_parser.format_help()) 121 print(MAIN_EXAMPLES % {'prog': self.progName}) 122 self._discovery_parser.print_help() 123 else: 124 print(self._main_parser.format_help()) 125 print(MODULE_EXAMPLES % {'prog': self.progName}) 126 127 def parseArgs(self, argv): 128 self._initArgParsers() 129 if self.module is None: 130 if len(argv) > 1 and argv[1].lower() == 'discover': 131 self._do_discovery(argv[2:]) 132 return 133 self._main_parser.parse_args(argv[1:], self) 134 if not self.tests: 135 # this allows "python -m unittest -v" to still work for 136 # test discovery. 137 self._do_discovery([]) 138 return 139 else: 140 self._main_parser.parse_args(argv[1:], self) 141 142 if self.tests: 143 self.testNames = _convert_names(self.tests) 144 if __name__ == '__main__': 145 # to support python -m unittest ... 146 self.module = None 147 elif self.defaultTest is None: 148 # createTests will load tests from self.module 149 self.testNames = None 150 elif isinstance(self.defaultTest, str): 151 self.testNames = (self.defaultTest,) 152 else: 153 self.testNames = list(self.defaultTest) 154 self.createTests() 155 156 def createTests(self, from_discovery=False, Loader=None): 157 if self.testNamePatterns: 158 self.testLoader.testNamePatterns = self.testNamePatterns 159 if from_discovery: 160 loader = self.testLoader if Loader is None else Loader() 161 self.test = loader.discover(self.start, self.pattern, self.top) 162 elif self.testNames is None: 163 self.test = self.testLoader.loadTestsFromModule(self.module) 164 else: 165 self.test = self.testLoader.loadTestsFromNames(self.testNames, 166 self.module) 167 168 def _initArgParsers(self): 169 parent_parser = self._getParentArgParser() 170 self._main_parser = self._getMainArgParser(parent_parser) 171 self._discovery_parser = self._getDiscoveryArgParser(parent_parser) 172 173 def _getParentArgParser(self): 174 parser = argparse.ArgumentParser(add_help=False) 175 176 parser.add_argument('-v', '--verbose', dest='verbosity', 177 action='store_const', const=2, 178 help='Verbose output') 179 parser.add_argument('-q', '--quiet', dest='verbosity', 180 action='store_const', const=0, 181 help='Quiet output') 182 parser.add_argument('--locals', dest='tb_locals', 183 action='store_true', 184 help='Show local variables in tracebacks') 185 parser.add_argument('--durations', dest='durations', type=int, 186 default=None, metavar="N", 187 help='Show the N slowest test cases (N=0 for all)') 188 if self.failfast is None: 189 parser.add_argument('-f', '--failfast', dest='failfast', 190 action='store_true', 191 help='Stop on first fail or error') 192 self.failfast = False 193 if self.catchbreak is None: 194 parser.add_argument('-c', '--catch', dest='catchbreak', 195 action='store_true', 196 help='Catch Ctrl-C and display results so far') 197 self.catchbreak = False 198 if self.buffer is None: 199 parser.add_argument('-b', '--buffer', dest='buffer', 200 action='store_true', 201 help='Buffer stdout and stderr during tests') 202 self.buffer = False 203 if self.testNamePatterns is None: 204 parser.add_argument('-k', dest='testNamePatterns', 205 action='append', type=_convert_select_pattern, 206 help='Only run tests which match the given substring') 207 self.testNamePatterns = [] 208 209 return parser 210 211 def _getMainArgParser(self, parent): 212 parser = argparse.ArgumentParser(parents=[parent]) 213 parser.prog = self.progName 214 parser.print_help = self._print_help 215 216 parser.add_argument('tests', nargs='*', 217 help='a list of any number of test modules, ' 218 'classes and test methods.') 219 220 return parser 221 222 def _getDiscoveryArgParser(self, parent): 223 parser = argparse.ArgumentParser(parents=[parent]) 224 parser.prog = '%s discover' % self.progName 225 parser.epilog = ('For test discovery all test modules must be ' 226 'importable from the top level directory of the ' 227 'project.') 228 229 parser.add_argument('-s', '--start-directory', dest='start', 230 help="Directory to start discovery ('.' default)") 231 parser.add_argument('-p', '--pattern', dest='pattern', 232 help="Pattern to match tests ('test*.py' default)") 233 parser.add_argument('-t', '--top-level-directory', dest='top', 234 help='Top level directory of project (defaults to ' 235 'start directory)') 236 for arg in ('start', 'pattern', 'top'): 237 parser.add_argument(arg, nargs='?', 238 default=argparse.SUPPRESS, 239 help=argparse.SUPPRESS) 240 241 return parser 242 243 def _do_discovery(self, argv, Loader=None): 244 self.start = '.' 245 self.pattern = 'test*.py' 246 self.top = None 247 if argv is not None: 248 # handle command line args for test discovery 249 if self._discovery_parser is None: 250 # for testing 251 self._initArgParsers() 252 self._discovery_parser.parse_args(argv, self) 253 254 self.createTests(from_discovery=True, Loader=Loader) 255 256 def runTests(self): 257 if self.catchbreak: 258 installHandler() 259 if self.testRunner is None: 260 self.testRunner = runner.TextTestRunner 261 if isinstance(self.testRunner, type): 262 try: 263 try: 264 testRunner = self.testRunner(verbosity=self.verbosity, 265 failfast=self.failfast, 266 buffer=self.buffer, 267 warnings=self.warnings, 268 tb_locals=self.tb_locals, 269 durations=self.durations) 270 except TypeError: 271 # didn't accept the tb_locals or durations argument 272 testRunner = self.testRunner(verbosity=self.verbosity, 273 failfast=self.failfast, 274 buffer=self.buffer, 275 warnings=self.warnings) 276 except TypeError: 277 # didn't accept the verbosity, buffer or failfast arguments 278 testRunner = self.testRunner() 279 else: 280 # it is assumed to be a TestRunner instance 281 testRunner = self.testRunner 282 self.result = testRunner.run(self.test) 283 if self.exit: 284 if self.result.testsRun == 0: 285 sys.exit(_NO_TESTS_EXITCODE) 286 elif self.result.wasSuccessful(): 287 sys.exit(0) 288 else: 289 sys.exit(1)
A command-line program that runs a set of tests; this is primarily for making test modules conveniently executable.
TestProgram( module='__main__', defaultTest=None, argv=None, testRunner=None, testLoader=<unittest.loader.TestLoader object>, exit=True, verbosity=1, failfast=None, catchbreak=None, buffer=None, warnings=None, *, tb_locals=False, durations=None)
68 def __init__(self, module='__main__', defaultTest=None, argv=None, 69 testRunner=None, testLoader=loader.defaultTestLoader, 70 exit=True, verbosity=1, failfast=None, catchbreak=None, 71 buffer=None, warnings=None, *, tb_locals=False, 72 durations=None): 73 if isinstance(module, str): 74 self.module = __import__(module) 75 for part in module.split('.')[1:]: 76 self.module = getattr(self.module, part) 77 else: 78 self.module = module 79 if argv is None: 80 argv = sys.argv 81 82 self.exit = exit 83 self.failfast = failfast 84 self.catchbreak = catchbreak 85 self.verbosity = verbosity 86 self.buffer = buffer 87 self.tb_locals = tb_locals 88 self.durations = durations 89 if warnings is None and not sys.warnoptions: 90 # even if DeprecationWarnings are ignored by default 91 # print them anyway unless other warnings settings are 92 # specified by the warnings arg or the -W python flag 93 self.warnings = 'default' 94 else: 95 # here self.warnings is set either to the value passed 96 # to the warnings args or to None. 97 # If the user didn't pass a value self.warnings will 98 # be None. This means that the behavior is unchanged 99 # and depends on the values passed to -W. 100 self.warnings = warnings 101 self.defaultTest = defaultTest 102 self.testRunner = testRunner 103 self.testLoader = testLoader 104 self.progName = os.path.basename(argv[0]) 105 self.parseArgs(argv) 106 self.runTests()
def
parseArgs(self, argv):
127 def parseArgs(self, argv): 128 self._initArgParsers() 129 if self.module is None: 130 if len(argv) > 1 and argv[1].lower() == 'discover': 131 self._do_discovery(argv[2:]) 132 return 133 self._main_parser.parse_args(argv[1:], self) 134 if not self.tests: 135 # this allows "python -m unittest -v" to still work for 136 # test discovery. 137 self._do_discovery([]) 138 return 139 else: 140 self._main_parser.parse_args(argv[1:], self) 141 142 if self.tests: 143 self.testNames = _convert_names(self.tests) 144 if __name__ == '__main__': 145 # to support python -m unittest ... 146 self.module = None 147 elif self.defaultTest is None: 148 # createTests will load tests from self.module 149 self.testNames = None 150 elif isinstance(self.defaultTest, str): 151 self.testNames = (self.defaultTest,) 152 else: 153 self.testNames = list(self.defaultTest) 154 self.createTests()
def
createTests(self, from_discovery=False, Loader=None):
156 def createTests(self, from_discovery=False, Loader=None): 157 if self.testNamePatterns: 158 self.testLoader.testNamePatterns = self.testNamePatterns 159 if from_discovery: 160 loader = self.testLoader if Loader is None else Loader() 161 self.test = loader.discover(self.start, self.pattern, self.top) 162 elif self.testNames is None: 163 self.test = self.testLoader.loadTestsFromModule(self.module) 164 else: 165 self.test = self.testLoader.loadTestsFromNames(self.testNames, 166 self.module)
def
runTests(self):
256 def runTests(self): 257 if self.catchbreak: 258 installHandler() 259 if self.testRunner is None: 260 self.testRunner = runner.TextTestRunner 261 if isinstance(self.testRunner, type): 262 try: 263 try: 264 testRunner = self.testRunner(verbosity=self.verbosity, 265 failfast=self.failfast, 266 buffer=self.buffer, 267 warnings=self.warnings, 268 tb_locals=self.tb_locals, 269 durations=self.durations) 270 except TypeError: 271 # didn't accept the tb_locals or durations argument 272 testRunner = self.testRunner(verbosity=self.verbosity, 273 failfast=self.failfast, 274 buffer=self.buffer, 275 warnings=self.warnings) 276 except TypeError: 277 # didn't accept the verbosity, buffer or failfast arguments 278 testRunner = self.testRunner() 279 else: 280 # it is assumed to be a TestRunner instance 281 testRunner = self.testRunner 282 self.result = testRunner.run(self.test) 283 if self.exit: 284 if self.result.testsRun == 0: 285 sys.exit(_NO_TESTS_EXITCODE) 286 elif self.result.wasSuccessful(): 287 sys.exit(0) 288 else: 289 sys.exit(1)
main =
<class 'TestProgram'>