1"""Test case implementation""" 2 3import sys 4import functools 5import difflib 6import logging 7import pprint 8import re 9import warnings 10import collections 11import contextlib 12import traceback 13 14from . import result 15from .util import (strclass, safe_repr, _count_diff_all_purpose, 16 _count_diff_hashable, _common_shorten_repr) 17 18__unittest = True 19 20_subtest_msg_sentinel = object() 21 22DIFF_OMITTED = ('\nDiff is %s characters long. ' 23 'Set self.maxDiff to None to see it.') 24 25class SkipTest(Exception): 26 """ 27 Raise this exception in a test to skip it. 28 29 Usually you can use TestCase.skipTest() or one of the skipping decorators 30 instead of raising this directly. 31 """ 32 33class _ShouldStop(Exception): 34 """ 35 The test should stop. 36 """ 37 38class _UnexpectedSuccess(Exception): 39 """ 40 The test was supposed to fail, but it didn't! 41 """ 42 43 44class _Outcome(object): 45 def __init__(self, result=None): 46 self.expecting_failure = False 47 self.result = result 48 self.result_supports_subtests = hasattr(result, "addSubTest") 49 self.success = True 50 self.skipped = [] 51 self.expectedFailure = None 52 self.errors = [] 53 54 @contextlib.contextmanager 55 def testPartExecutor(self, test_case, isTest=False): 56 old_success = self.success 57 self.success = True 58 try: 59 yield 60 except KeyboardInterrupt: 61 raise 62 except SkipTest as e: 63 self.success = False 64 self.skipped.append((test_case, str(e))) 65 except _ShouldStop: 66 pass 67 except: 68 exc_info = sys.exc_info() 69 if self.expecting_failure: 70 self.expectedFailure = exc_info 71 else: 72 self.success = False 73 self.errors.append((test_case, exc_info)) 74 # explicitly break a reference cycle: 75 # exc_info -> frame -> exc_info 76 exc_info = None 77 else: 78 if self.result_supports_subtests and self.success: 79 self.errors.append((test_case, None)) 80 finally: 81 self.success = self.success and old_success 82 83 84def _id(obj): 85 return obj 86 87def skip(reason): 88 """ 89 Unconditionally skip a test. 90 """ 91 def decorator(test_item): 92 if not isinstance(test_item, type): 93 @functools.wraps(test_item) 94 def skip_wrapper(*args, **kwargs): 95 raise SkipTest(reason) 96 test_item = skip_wrapper 97 98 test_item.__unittest_skip__ = True 99 test_item.__unittest_skip_why__ = reason 100 return test_item 101 return decorator 102 103def skipIf(condition, reason): 104 """ 105 Skip a test if the condition is true. 106 """ 107 if condition: 108 return skip(reason) 109 return _id 110 111def skipUnless(condition, reason): 112 """ 113 Skip a test unless the condition is true. 114 """ 115 if not condition: 116 return skip(reason) 117 return _id 118 119def expectedFailure(test_item): 120 test_item.__unittest_expecting_failure__ = True 121 return test_item 122 123def _is_subtype(expected, basetype): 124 if isinstance(expected, tuple): 125 return all(_is_subtype(e, basetype) for e in expected) 126 return isinstance(expected, type) and issubclass(expected, basetype) 127 128class _BaseTestCaseContext: 129 130 def __init__(self, test_case): 131 self.test_case = test_case 132 133 def _raiseFailure(self, standardMsg): 134 msg = self.test_case._formatMessage(self.msg, standardMsg) 135 raise self.test_case.failureException(msg) 136 137class _AssertRaisesBaseContext(_BaseTestCaseContext): 138 139 def __init__(self, expected, test_case, expected_regex=None): 140 _BaseTestCaseContext.__init__(self, test_case) 141 self.expected = expected 142 self.test_case = test_case 143 if expected_regex is not None: 144 expected_regex = re.compile(expected_regex) 145 self.expected_regex = expected_regex 146 self.obj_name = None 147 self.msg = None 148 149 def handle(self, name, args, kwargs): 150 """ 151 If args is empty, assertRaises/Warns is being used as a 152 context manager, so check for a 'msg' kwarg and return self. 153 If args is not empty, call a callable passing positional and keyword 154 arguments. 155 """ 156 if not _is_subtype(self.expected, self._base_type): 157 raise TypeError('%s() arg 1 must be %s' % 158 (name, self._base_type_str)) 159 if args and args[0] is None: 160 warnings.warn("callable is None", 161 DeprecationWarning, 3) 162 args = () 163 if not args: 164 self.msg = kwargs.pop('msg', None) 165 if kwargs: 166 warnings.warn('%r is an invalid keyword argument for ' 167 'this function' % next(iter(kwargs)), 168 DeprecationWarning, 3) 169 return self 170 171 callable_obj, *args = args 172 try: 173 self.obj_name = callable_obj.__name__ 174 except AttributeError: 175 self.obj_name = str(callable_obj) 176 with self: 177 callable_obj(*args, **kwargs) 178 179 180class _AssertRaisesContext(_AssertRaisesBaseContext): 181 """A context manager used to implement TestCase.assertRaises* methods.""" 182 183 _base_type = BaseException 184 _base_type_str = 'an exception type or tuple of exception types' 185 186 def __enter__(self): 187 return self 188 189 def __exit__(self, exc_type, exc_value, tb): 190 if exc_type is None: 191 try: 192 exc_name = self.expected.__name__ 193 except AttributeError: 194 exc_name = str(self.expected) 195 if self.obj_name: 196 self._raiseFailure("{} not raised by {}".format(exc_name, 197 self.obj_name)) 198 else: 199 self._raiseFailure("{} not raised".format(exc_name)) 200 else: 201 traceback.clear_frames(tb) 202 if not issubclass(exc_type, self.expected): 203 # let unexpected exceptions pass through 204 return False 205 # store exception, without traceback, for later retrieval 206 self.exception = exc_value.with_traceback(None) 207 if self.expected_regex is None: 208 return True 209 210 expected_regex = self.expected_regex 211 if not expected_regex.search(str(exc_value)): 212 self._raiseFailure('"{}" does not match "{}"'.format( 213 expected_regex.pattern, str(exc_value))) 214 return True 215 216 217class _AssertWarnsContext(_AssertRaisesBaseContext): 218 """A context manager used to implement TestCase.assertWarns* methods.""" 219 220 _base_type = Warning 221 _base_type_str = 'a warning type or tuple of warning types' 222 223 def __enter__(self): 224 # The __warningregistry__'s need to be in a pristine state for tests 225 # to work properly. 226 for v in sys.modules.values(): 227 if getattr(v, '__warningregistry__', None): 228 v.__warningregistry__ = {} 229 self.warnings_manager = warnings.catch_warnings(record=True) 230 self.warnings = self.warnings_manager.__enter__() 231 warnings.simplefilter("always", self.expected) 232 return self 233 234 def __exit__(self, exc_type, exc_value, tb): 235 self.warnings_manager.__exit__(exc_type, exc_value, tb) 236 if exc_type is not None: 237 # let unexpected exceptions pass through 238 return 239 try: 240 exc_name = self.expected.__name__ 241 except AttributeError: 242 exc_name = str(self.expected) 243 first_matching = None 244 for m in self.warnings: 245 w = m.message 246 if not isinstance(w, self.expected): 247 continue 248 if first_matching is None: 249 first_matching = w 250 if (self.expected_regex is not None and 251 not self.expected_regex.search(str(w))): 252 continue 253 # store warning for later retrieval 254 self.warning = w 255 self.filename = m.filename 256 self.lineno = m.lineno 257 return 258 # Now we simply try to choose a helpful failure message 259 if first_matching is not None: 260 self._raiseFailure('"{}" does not match "{}"'.format( 261 self.expected_regex.pattern, str(first_matching))) 262 if self.obj_name: 263 self._raiseFailure("{} not triggered by {}".format(exc_name, 264 self.obj_name)) 265 else: 266 self._raiseFailure("{} not triggered".format(exc_name)) 267 268 269 270_LoggingWatcher = collections.namedtuple("_LoggingWatcher", 271 ["records", "output"]) 272 273 274class _CapturingHandler(logging.Handler): 275 """ 276 A logging handler capturing all (raw and formatted) logging output. 277 """ 278 279 def __init__(self): 280 logging.Handler.__init__(self) 281 self.watcher = _LoggingWatcher([], []) 282 283 def flush(self): 284 pass 285 286 def emit(self, record): 287 self.watcher.records.append(record) 288 msg = self.format(record) 289 self.watcher.output.append(msg) 290 291 292 293class _AssertLogsContext(_BaseTestCaseContext): 294 """A context manager used to implement TestCase.assertLogs().""" 295 296 LOGGING_FORMAT = "%(levelname)s:%(name)s:%(message)s" 297 298 def __init__(self, test_case, logger_name, level): 299 _BaseTestCaseContext.__init__(self, test_case) 300 self.logger_name = logger_name 301 if level: 302 self.level = logging._nameToLevel.get(level, level) 303 else: 304 self.level = logging.INFO 305 self.msg = None 306 307 def __enter__(self): 308 if isinstance(self.logger_name, logging.Logger): 309 logger = self.logger = self.logger_name 310 else: 311 logger = self.logger = logging.getLogger(self.logger_name) 312 formatter = logging.Formatter(self.LOGGING_FORMAT) 313 handler = _CapturingHandler() 314 handler.setFormatter(formatter) 315 self.watcher = handler.watcher 316 self.old_handlers = logger.handlers[:] 317 self.old_level = logger.level 318 self.old_propagate = logger.propagate 319 logger.handlers = [handler] 320 logger.setLevel(self.level) 321 logger.propagate = False 322 return handler.watcher 323 324 def __exit__(self, exc_type, exc_value, tb): 325 self.logger.handlers = self.old_handlers 326 self.logger.propagate = self.old_propagate 327 self.logger.setLevel(self.old_level) 328 if exc_type is not None: 329 # let unexpected exceptions pass through 330 return False 331 if len(self.watcher.records) == 0: 332 self._raiseFailure( 333 "no logs of level {} or higher triggered on {}" 334 .format(logging.getLevelName(self.level), self.logger.name)) 335 336 337class TestCase(object): 338 """A class whose instances are single test cases. 339 340 By default, the test code itself should be placed in a method named 341 'runTest'. 342 343 If the fixture may be used for many test cases, create as 344 many test methods as are needed. When instantiating such a TestCase 345 subclass, specify in the constructor arguments the name of the test method 346 that the instance is to execute. 347 348 Test authors should subclass TestCase for their own tests. Construction 349 and deconstruction of the test's environment ('fixture') can be 350 implemented by overriding the 'setUp' and 'tearDown' methods respectively. 351 352 If it is necessary to override the __init__ method, the base class 353 __init__ method must always be called. It is important that subclasses 354 should not change the signature of their __init__ method, since instances 355 of the classes are instantiated automatically by parts of the framework 356 in order to be run. 357 358 When subclassing TestCase, you can set these attributes: 359 * failureException: determines which exception will be raised when 360 the instance's assertion methods fail; test methods raising this 361 exception will be deemed to have 'failed' rather than 'errored'. 362 * longMessage: determines whether long messages (including repr of 363 objects used in assert methods) will be printed on failure in *addition* 364 to any explicit message passed. 365 * maxDiff: sets the maximum length of a diff in failure messages 366 by assert methods using difflib. It is looked up as an instance 367 attribute so can be configured by individual tests if required. 368 """ 369 370 failureException = AssertionError 371 372 longMessage = True 373 374 maxDiff = 80*8 375 376 # If a string is longer than _diffThreshold, use normal comparison instead 377 # of difflib. See #11763. 378 _diffThreshold = 2**16 379 380 # Attribute used by TestSuite for classSetUp 381 382 _classSetupFailed = False 383 384 def __init__(self, methodName='runTest'): 385 """Create an instance of the class that will use the named test 386 method when executed. Raises a ValueError if the instance does 387 not have a method with the specified name. 388 """ 389 self._testMethodName = methodName 390 self._outcome = None 391 self._testMethodDoc = 'No test' 392 try: 393 testMethod = getattr(self, methodName) 394 except AttributeError: 395 if methodName != 'runTest': 396 # we allow instantiation with no explicit method name 397 # but not an *incorrect* or missing method name 398 raise ValueError("no such test method in %s: %s" % 399 (self.__class__, methodName)) 400 else: 401 self._testMethodDoc = testMethod.__doc__ 402 self._cleanups = [] 403 self._subtest = None 404 405 # Map types to custom assertEqual functions that will compare 406 # instances of said type in more detail to generate a more useful 407 # error message. 408 self._type_equality_funcs = {} 409 self.addTypeEqualityFunc(dict, 'assertDictEqual') 410 self.addTypeEqualityFunc(list, 'assertListEqual') 411 self.addTypeEqualityFunc(tuple, 'assertTupleEqual') 412 self.addTypeEqualityFunc(set, 'assertSetEqual') 413 self.addTypeEqualityFunc(frozenset, 'assertSetEqual') 414 self.addTypeEqualityFunc(str, 'assertMultiLineEqual') 415 416 def addTypeEqualityFunc(self, typeobj, function): 417 """Add a type specific assertEqual style function to compare a type. 418 419 This method is for use by TestCase subclasses that need to register 420 their own type equality functions to provide nicer error messages. 421 422 Args: 423 typeobj: The data type to call this function on when both values 424 are of the same type in assertEqual(). 425 function: The callable taking two arguments and an optional 426 msg= argument that raises self.failureException with a 427 useful error message when the two arguments are not equal. 428 """ 429 self._type_equality_funcs[typeobj] = function 430 431 def addCleanup(self, function, *args, **kwargs): 432 """Add a function, with arguments, to be called when the test is 433 completed. Functions added are called on a LIFO basis and are 434 called after tearDown on test failure or success. 435 436 Cleanup items are called even if setUp fails (unlike tearDown).""" 437 self._cleanups.append((function, args, kwargs)) 438 439 def setUp(self): 440 "Hook method for setting up the test fixture before exercising it." 441 pass 442 443 def tearDown(self): 444 "Hook method for deconstructing the test fixture after testing it." 445 pass 446 447 @classmethod 448 def setUpClass(cls): 449 "Hook method for setting up class fixture before running tests in the class." 450 451 @classmethod 452 def tearDownClass(cls): 453 "Hook method for deconstructing the class fixture after running all tests in the class." 454 455 def countTestCases(self): 456 return 1 457 458 def defaultTestResult(self): 459 return result.TestResult() 460 461 def shortDescription(self): 462 """Returns a one-line description of the test, or None if no 463 description has been provided. 464 465 The default implementation of this method returns the first line of 466 the specified test method's docstring. 467 """ 468 doc = self._testMethodDoc 469 return doc and doc.split("\n")[0].strip() or None 470 471 472 def id(self): 473 return "%s.%s" % (strclass(self.__class__), self._testMethodName) 474 475 def __eq__(self, other): 476 if type(self) is not type(other): 477 return NotImplemented 478 479 return self._testMethodName == other._testMethodName 480 481 def __hash__(self): 482 return hash((type(self), self._testMethodName)) 483 484 def __str__(self): 485 return "%s (%s)" % (self._testMethodName, strclass(self.__class__)) 486 487 def __repr__(self): 488 return "<%s testMethod=%s>" % \ 489 (strclass(self.__class__), self._testMethodName) 490 491 def _addSkip(self, result, test_case, reason): 492 addSkip = getattr(result, 'addSkip', None) 493 if addSkip is not None: 494 addSkip(test_case, reason) 495 else: 496 warnings.warn("TestResult has no addSkip method, skips not reported", 497 RuntimeWarning, 2) 498 result.addSuccess(test_case) 499 500 @contextlib.contextmanager 501 def subTest(self, msg=_subtest_msg_sentinel, **params): 502 """Return a context manager that will return the enclosed block 503 of code in a subtest identified by the optional message and 504 keyword parameters. A failure in the subtest marks the test 505 case as failed but resumes execution at the end of the enclosed 506 block, allowing further test code to be executed. 507 """ 508 if not self._outcome.result_supports_subtests: 509 yield 510 return 511 parent = self._subtest 512 if parent is None: 513 params_map = collections.ChainMap(params) 514 else: 515 params_map = parent.params.new_child(params) 516 self._subtest = _SubTest(self, msg, params_map) 517 try: 518 with self._outcome.testPartExecutor(self._subtest, isTest=True): 519 yield 520 if not self._outcome.success: 521 result = self._outcome.result 522 if result is not None and result.failfast: 523 raise _ShouldStop 524 elif self._outcome.expectedFailure: 525 # If the test is expecting a failure, we really want to 526 # stop now and register the expected failure. 527 raise _ShouldStop 528 finally: 529 self._subtest = parent 530 531 def _feedErrorsToResult(self, result, errors): 532 for test, exc_info in errors: 533 if isinstance(test, _SubTest): 534 result.addSubTest(test.test_case, test, exc_info) 535 elif exc_info is not None: 536 if issubclass(exc_info[0], self.failureException): 537 result.addFailure(test, exc_info) 538 else: 539 result.addError(test, exc_info) 540 541 def _addExpectedFailure(self, result, exc_info): 542 try: 543 addExpectedFailure = result.addExpectedFailure 544 except AttributeError: 545 warnings.warn("TestResult has no addExpectedFailure method, reporting as passes", 546 RuntimeWarning) 547 result.addSuccess(self) 548 else: 549 addExpectedFailure(self, exc_info) 550 551 def _addUnexpectedSuccess(self, result): 552 try: 553 addUnexpectedSuccess = result.addUnexpectedSuccess 554 except AttributeError: 555 warnings.warn("TestResult has no addUnexpectedSuccess method, reporting as failure", 556 RuntimeWarning) 557 # We need to pass an actual exception and traceback to addFailure, 558 # otherwise the legacy result can choke. 559 try: 560 raise _UnexpectedSuccess from None 561 except _UnexpectedSuccess: 562 result.addFailure(self, sys.exc_info()) 563 else: 564 addUnexpectedSuccess(self) 565 566 def run(self, result=None): 567 orig_result = result 568 if result is None: 569 result = self.defaultTestResult() 570 startTestRun = getattr(result, 'startTestRun', None) 571 if startTestRun is not None: 572 startTestRun() 573 574 result.startTest(self) 575 576 testMethod = getattr(self, self._testMethodName) 577 if (getattr(self.__class__, "__unittest_skip__", False) or 578 getattr(testMethod, "__unittest_skip__", False)): 579 # If the class or method was skipped. 580 try: 581 skip_why = (getattr(self.__class__, '__unittest_skip_why__', '') 582 or getattr(testMethod, '__unittest_skip_why__', '')) 583 self._addSkip(result, self, skip_why) 584 finally: 585 result.stopTest(self) 586 return 587 expecting_failure_method = getattr(testMethod, 588 "__unittest_expecting_failure__", False) 589 expecting_failure_class = getattr(self, 590 "__unittest_expecting_failure__", False) 591 expecting_failure = expecting_failure_class or expecting_failure_method 592 outcome = _Outcome(result) 593 try: 594 self._outcome = outcome 595 596 with outcome.testPartExecutor(self): 597 self.setUp() 598 if outcome.success: 599 outcome.expecting_failure = expecting_failure 600 with outcome.testPartExecutor(self, isTest=True): 601 testMethod() 602 outcome.expecting_failure = False 603 with outcome.testPartExecutor(self): 604 self.tearDown() 605 606 self.doCleanups() 607 for test, reason in outcome.skipped: 608 self._addSkip(result, test, reason) 609 self._feedErrorsToResult(result, outcome.errors) 610 if outcome.success: 611 if expecting_failure: 612 if outcome.expectedFailure: 613 self._addExpectedFailure(result, outcome.expectedFailure) 614 else: 615 self._addUnexpectedSuccess(result) 616 else: 617 result.addSuccess(self) 618 return result 619 finally: 620 result.stopTest(self) 621 if orig_result is None: 622 stopTestRun = getattr(result, 'stopTestRun', None) 623 if stopTestRun is not None: 624 stopTestRun() 625 626 # explicitly break reference cycles: 627 # outcome.errors -> frame -> outcome -> outcome.errors 628 # outcome.expectedFailure -> frame -> outcome -> outcome.expectedFailure 629 outcome.errors.clear() 630 outcome.expectedFailure = None 631 632 # clear the outcome, no more needed 633 self._outcome = None 634 635 def doCleanups(self): 636 """Execute all cleanup functions. Normally called for you after 637 tearDown.""" 638 outcome = self._outcome or _Outcome() 639 while self._cleanups: 640 function, args, kwargs = self._cleanups.pop() 641 with outcome.testPartExecutor(self): 642 function(*args, **kwargs) 643 644 # return this for backwards compatibility 645 # even though we no longer us it internally 646 return outcome.success 647 648 def __call__(self, *args, **kwds): 649 return self.run(*args, **kwds) 650 651 def debug(self): 652 """Run the test without collecting errors in a TestResult""" 653 self.setUp() 654 getattr(self, self._testMethodName)() 655 self.tearDown() 656 while self._cleanups: 657 function, args, kwargs = self._cleanups.pop(-1) 658 function(*args, **kwargs) 659 660 def skipTest(self, reason): 661 """Skip this test.""" 662 raise SkipTest(reason) 663 664 def fail(self, msg=None): 665 """Fail immediately, with the given message.""" 666 raise self.failureException(msg) 667 668 def assertFalse(self, expr, msg=None): 669 """Check that the expression is false.""" 670 if expr: 671 msg = self._formatMessage(msg, "%s is not false" % safe_repr(expr)) 672 raise self.failureException(msg) 673 674 def assertTrue(self, expr, msg=None): 675 """Check that the expression is true.""" 676 if not expr: 677 msg = self._formatMessage(msg, "%s is not true" % safe_repr(expr)) 678 raise self.failureException(msg) 679 680 def _formatMessage(self, msg, standardMsg): 681 """Honour the longMessage attribute when generating failure messages. 682 If longMessage is False this means: 683 * Use only an explicit message if it is provided 684 * Otherwise use the standard message for the assert 685 686 If longMessage is True: 687 * Use the standard message 688 * If an explicit message is provided, plus ' : ' and the explicit message 689 """ 690 if not self.longMessage: 691 return msg or standardMsg 692 if msg is None: 693 return standardMsg 694 try: 695 # don't switch to '{}' formatting in Python 2.X 696 # it changes the way unicode input is handled 697 return '%s : %s' % (standardMsg, msg) 698 except UnicodeDecodeError: 699 return '%s : %s' % (safe_repr(standardMsg), safe_repr(msg)) 700 701 def assertRaises(self, expected_exception, *args, **kwargs): 702 """Fail unless an exception of class expected_exception is raised 703 by the callable when invoked with specified positional and 704 keyword arguments. If a different type of exception is 705 raised, it will not be caught, and the test case will be 706 deemed to have suffered an error, exactly as for an 707 unexpected exception. 708 709 If called with the callable and arguments omitted, will return a 710 context object used like this:: 711 712 with self.assertRaises(SomeException): 713 do_something() 714 715 An optional keyword argument 'msg' can be provided when assertRaises 716 is used as a context object. 717 718 The context manager keeps a reference to the exception as 719 the 'exception' attribute. This allows you to inspect the 720 exception after the assertion:: 721 722 with self.assertRaises(SomeException) as cm: 723 do_something() 724 the_exception = cm.exception 725 self.assertEqual(the_exception.error_code, 3) 726 """ 727 context = _AssertRaisesContext(expected_exception, self) 728 return context.handle('assertRaises', args, kwargs) 729 730 def assertWarns(self, expected_warning, *args, **kwargs): 731 """Fail unless a warning of class warnClass is triggered 732 by the callable when invoked with specified positional and 733 keyword arguments. If a different type of warning is 734 triggered, it will not be handled: depending on the other 735 warning filtering rules in effect, it might be silenced, printed 736 out, or raised as an exception. 737 738 If called with the callable and arguments omitted, will return a 739 context object used like this:: 740 741 with self.assertWarns(SomeWarning): 742 do_something() 743 744 An optional keyword argument 'msg' can be provided when assertWarns 745 is used as a context object. 746 747 The context manager keeps a reference to the first matching 748 warning as the 'warning' attribute; similarly, the 'filename' 749 and 'lineno' attributes give you information about the line 750 of Python code from which the warning was triggered. 751 This allows you to inspect the warning after the assertion:: 752 753 with self.assertWarns(SomeWarning) as cm: 754 do_something() 755 the_warning = cm.warning 756 self.assertEqual(the_warning.some_attribute, 147) 757 """ 758 context = _AssertWarnsContext(expected_warning, self) 759 return context.handle('assertWarns', args, kwargs) 760 761 def assertLogs(self, logger=None, level=None): 762 """Fail unless a log message of level *level* or higher is emitted 763 on *logger_name* or its children. If omitted, *level* defaults to 764 INFO and *logger* defaults to the root logger. 765 766 This method must be used as a context manager, and will yield 767 a recording object with two attributes: `output` and `records`. 768 At the end of the context manager, the `output` attribute will 769 be a list of the matching formatted log messages and the 770 `records` attribute will be a list of the corresponding LogRecord 771 objects. 772 773 Example:: 774 775 with self.assertLogs('foo', level='INFO') as cm: 776 logging.getLogger('foo').info('first message') 777 logging.getLogger('foo.bar').error('second message') 778 self.assertEqual(cm.output, ['INFO:foo:first message', 779 'ERROR:foo.bar:second message']) 780 """ 781 return _AssertLogsContext(self, logger, level) 782 783 def _getAssertEqualityFunc(self, first, second): 784 """Get a detailed comparison function for the types of the two args. 785 786 Returns: A callable accepting (first, second, msg=None) that will 787 raise a failure exception if first != second with a useful human 788 readable error message for those types. 789 """ 790 # 791 # NOTE(gregory.p.smith): I considered isinstance(first, type(second)) 792 # and vice versa. I opted for the conservative approach in case 793 # subclasses are not intended to be compared in detail to their super 794 # class instances using a type equality func. This means testing 795 # subtypes won't automagically use the detailed comparison. Callers 796 # should use their type specific assertSpamEqual method to compare 797 # subclasses if the detailed comparison is desired and appropriate. 798 # See the discussion in http://bugs.python.org/issue2578. 799 # 800 if type(first) is type(second): 801 asserter = self._type_equality_funcs.get(type(first)) 802 if asserter is not None: 803 if isinstance(asserter, str): 804 asserter = getattr(self, asserter) 805 return asserter 806 807 return self._baseAssertEqual 808 809 def _baseAssertEqual(self, first, second, msg=None): 810 """The default assertEqual implementation, not type specific.""" 811 if not first == second: 812 standardMsg = '%s != %s' % _common_shorten_repr(first, second) 813 msg = self._formatMessage(msg, standardMsg) 814 raise self.failureException(msg) 815 816 def assertEqual(self, first, second, msg=None): 817 """Fail if the two objects are unequal as determined by the '==' 818 operator. 819 """ 820 assertion_func = self._getAssertEqualityFunc(first, second) 821 assertion_func(first, second, msg=msg) 822 823 def assertNotEqual(self, first, second, msg=None): 824 """Fail if the two objects are equal as determined by the '!=' 825 operator. 826 """ 827 if not first != second: 828 msg = self._formatMessage(msg, '%s == %s' % (safe_repr(first), 829 safe_repr(second))) 830 raise self.failureException(msg) 831 832 def assertAlmostEqual(self, first, second, places=None, msg=None, 833 delta=None): 834 """Fail if the two objects are unequal as determined by their 835 difference rounded to the given number of decimal places 836 (default 7) and comparing to zero, or by comparing that the 837 between the two objects is more than the given delta. 838 839 Note that decimal places (from zero) are usually not the same 840 as significant digits (measured from the most significant digit). 841 842 If the two objects compare equal then they will automatically 843 compare almost equal. 844 """ 845 if first == second: 846 # shortcut 847 return 848 if delta is not None and places is not None: 849 raise TypeError("specify delta or places not both") 850 851 if delta is not None: 852 if abs(first - second) <= delta: 853 return 854 855 standardMsg = '%s != %s within %s delta' % (safe_repr(first), 856 safe_repr(second), 857 safe_repr(delta)) 858 else: 859 if places is None: 860 places = 7 861 862 if round(abs(second-first), places) == 0: 863 return 864 865 standardMsg = '%s != %s within %r places' % (safe_repr(first), 866 safe_repr(second), 867 places) 868 msg = self._formatMessage(msg, standardMsg) 869 raise self.failureException(msg) 870 871 def assertNotAlmostEqual(self, first, second, places=None, msg=None, 872 delta=None): 873 """Fail if the two objects are equal as determined by their 874 difference rounded to the given number of decimal places 875 (default 7) and comparing to zero, or by comparing that the 876 between the two objects is less than the given delta. 877 878 Note that decimal places (from zero) are usually not the same 879 as significant digits (measured from the most significant digit). 880 881 Objects that are equal automatically fail. 882 """ 883 if delta is not None and places is not None: 884 raise TypeError("specify delta or places not both") 885 if delta is not None: 886 if not (first == second) and abs(first - second) > delta: 887 return 888 standardMsg = '%s == %s within %s delta' % (safe_repr(first), 889 safe_repr(second), 890 safe_repr(delta)) 891 else: 892 if places is None: 893 places = 7 894 if not (first == second) and round(abs(second-first), places) != 0: 895 return 896 standardMsg = '%s == %s within %r places' % (safe_repr(first), 897 safe_repr(second), 898 places) 899 900 msg = self._formatMessage(msg, standardMsg) 901 raise self.failureException(msg) 902 903 904 def assertSequenceEqual(self, seq1, seq2, msg=None, seq_type=None): 905 """An equality assertion for ordered sequences (like lists and tuples). 906 907 For the purposes of this function, a valid ordered sequence type is one 908 which can be indexed, has a length, and has an equality operator. 909 910 Args: 911 seq1: The first sequence to compare. 912 seq2: The second sequence to compare. 913 seq_type: The expected datatype of the sequences, or None if no 914 datatype should be enforced. 915 msg: Optional message to use on failure instead of a list of 916 differences. 917 """ 918 if seq_type is not None: 919 seq_type_name = seq_type.__name__ 920 if not isinstance(seq1, seq_type): 921 raise self.failureException('First sequence is not a %s: %s' 922 % (seq_type_name, safe_repr(seq1))) 923 if not isinstance(seq2, seq_type): 924 raise self.failureException('Second sequence is not a %s: %s' 925 % (seq_type_name, safe_repr(seq2))) 926 else: 927 seq_type_name = "sequence" 928 929 differing = None 930 try: 931 len1 = len(seq1) 932 except (TypeError, NotImplementedError): 933 differing = 'First %s has no length. Non-sequence?' % ( 934 seq_type_name) 935 936 if differing is None: 937 try: 938 len2 = len(seq2) 939 except (TypeError, NotImplementedError): 940 differing = 'Second %s has no length. Non-sequence?' % ( 941 seq_type_name) 942 943 if differing is None: 944 if seq1 == seq2: 945 return 946 947 differing = '%ss differ: %s != %s\n' % ( 948 (seq_type_name.capitalize(),) + 949 _common_shorten_repr(seq1, seq2)) 950 951 for i in range(min(len1, len2)): 952 try: 953 item1 = seq1[i] 954 except (TypeError, IndexError, NotImplementedError): 955 differing += ('\nUnable to index element %d of first %s\n' % 956 (i, seq_type_name)) 957 break 958 959 try: 960 item2 = seq2[i] 961 except (TypeError, IndexError, NotImplementedError): 962 differing += ('\nUnable to index element %d of second %s\n' % 963 (i, seq_type_name)) 964 break 965 966 if item1 != item2: 967 differing += ('\nFirst differing element %d:\n%s\n%s\n' % 968 ((i,) + _common_shorten_repr(item1, item2))) 969 break 970 else: 971 if (len1 == len2 and seq_type is None and 972 type(seq1) != type(seq2)): 973 # The sequences are the same, but have differing types. 974 return 975 976 if len1 > len2: 977 differing += ('\nFirst %s contains %d additional ' 978 'elements.\n' % (seq_type_name, len1 - len2)) 979 try: 980 differing += ('First extra element %d:\n%s\n' % 981 (len2, safe_repr(seq1[len2]))) 982 except (TypeError, IndexError, NotImplementedError): 983 differing += ('Unable to index element %d ' 984 'of first %s\n' % (len2, seq_type_name)) 985 elif len1 < len2: 986 differing += ('\nSecond %s contains %d additional ' 987 'elements.\n' % (seq_type_name, len2 - len1)) 988 try: 989 differing += ('First extra element %d:\n%s\n' % 990 (len1, safe_repr(seq2[len1]))) 991 except (TypeError, IndexError, NotImplementedError): 992 differing += ('Unable to index element %d ' 993 'of second %s\n' % (len1, seq_type_name)) 994 standardMsg = differing 995 diffMsg = '\n' + '\n'.join( 996 difflib.ndiff(pprint.pformat(seq1).splitlines(), 997 pprint.pformat(seq2).splitlines())) 998 999 standardMsg = self._truncateMessage(standardMsg, diffMsg) 1000 msg = self._formatMessage(msg, standardMsg) 1001 self.fail(msg) 1002 1003 def _truncateMessage(self, message, diff): 1004 max_diff = self.maxDiff 1005 if max_diff is None or len(diff) <= max_diff: 1006 return message + diff 1007 return message + (DIFF_OMITTED % len(diff)) 1008 1009 def assertListEqual(self, list1, list2, msg=None): 1010 """A list-specific equality assertion. 1011 1012 Args: 1013 list1: The first list to compare. 1014 list2: The second list to compare. 1015 msg: Optional message to use on failure instead of a list of 1016 differences. 1017 1018 """ 1019 self.assertSequenceEqual(list1, list2, msg, seq_type=list) 1020 1021 def assertTupleEqual(self, tuple1, tuple2, msg=None): 1022 """A tuple-specific equality assertion. 1023 1024 Args: 1025 tuple1: The first tuple to compare. 1026 tuple2: The second tuple to compare. 1027 msg: Optional message to use on failure instead of a list of 1028 differences. 1029 """ 1030 self.assertSequenceEqual(tuple1, tuple2, msg, seq_type=tuple) 1031 1032 def assertSetEqual(self, set1, set2, msg=None): 1033 """A set-specific equality assertion. 1034 1035 Args: 1036 set1: The first set to compare. 1037 set2: The second set to compare. 1038 msg: Optional message to use on failure instead of a list of 1039 differences. 1040 1041 assertSetEqual uses ducktyping to support different types of sets, and 1042 is optimized for sets specifically (parameters must support a 1043 difference method). 1044 """ 1045 try: 1046 difference1 = set1.difference(set2) 1047 except TypeError as e: 1048 self.fail('invalid type when attempting set difference: %s' % e) 1049 except AttributeError as e: 1050 self.fail('first argument does not support set difference: %s' % e) 1051 1052 try: 1053 difference2 = set2.difference(set1) 1054 except TypeError as e: 1055 self.fail('invalid type when attempting set difference: %s' % e) 1056 except AttributeError as e: 1057 self.fail('second argument does not support set difference: %s' % e) 1058 1059 if not (difference1 or difference2): 1060 return 1061 1062 lines = [] 1063 if difference1: 1064 lines.append('Items in the first set but not the second:') 1065 for item in difference1: 1066 lines.append(repr(item)) 1067 if difference2: 1068 lines.append('Items in the second set but not the first:') 1069 for item in difference2: 1070 lines.append(repr(item)) 1071 1072 standardMsg = '\n'.join(lines) 1073 self.fail(self._formatMessage(msg, standardMsg)) 1074 1075 def assertIn(self, member, container, msg=None): 1076 """Just like self.assertTrue(a in b), but with a nicer default message.""" 1077 if member not in container: 1078 standardMsg = '%s not found in %s' % (safe_repr(member), 1079 safe_repr(container)) 1080 self.fail(self._formatMessage(msg, standardMsg)) 1081 1082 def assertNotIn(self, member, container, msg=None): 1083 """Just like self.assertTrue(a not in b), but with a nicer default message.""" 1084 if member in container: 1085 standardMsg = '%s unexpectedly found in %s' % (safe_repr(member), 1086 safe_repr(container)) 1087 self.fail(self._formatMessage(msg, standardMsg)) 1088 1089 def assertIs(self, expr1, expr2, msg=None): 1090 """Just like self.assertTrue(a is b), but with a nicer default message.""" 1091 if expr1 is not expr2: 1092 standardMsg = '%s is not %s' % (safe_repr(expr1), 1093 safe_repr(expr2)) 1094 self.fail(self._formatMessage(msg, standardMsg)) 1095 1096 def assertIsNot(self, expr1, expr2, msg=None): 1097 """Just like self.assertTrue(a is not b), but with a nicer default message.""" 1098 if expr1 is expr2: 1099 standardMsg = 'unexpectedly identical: %s' % (safe_repr(expr1),) 1100 self.fail(self._formatMessage(msg, standardMsg)) 1101 1102 def assertDictEqual(self, d1, d2, msg=None): 1103 self.assertIsInstance(d1, dict, 'First argument is not a dictionary') 1104 self.assertIsInstance(d2, dict, 'Second argument is not a dictionary') 1105 1106 if d1 != d2: 1107 standardMsg = '%s != %s' % _common_shorten_repr(d1, d2) 1108 diff = ('\n' + '\n'.join(difflib.ndiff( 1109 pprint.pformat(d1).splitlines(), 1110 pprint.pformat(d2).splitlines()))) 1111 standardMsg = self._truncateMessage(standardMsg, diff) 1112 self.fail(self._formatMessage(msg, standardMsg)) 1113 1114 def assertDictContainsSubset(self, subset, dictionary, msg=None): 1115 """Checks whether dictionary is a superset of subset.""" 1116 warnings.warn('assertDictContainsSubset is deprecated', 1117 DeprecationWarning) 1118 missing = [] 1119 mismatched = [] 1120 for key, value in subset.items(): 1121 if key not in dictionary: 1122 missing.append(key) 1123 elif value != dictionary[key]: 1124 mismatched.append('%s, expected: %s, actual: %s' % 1125 (safe_repr(key), safe_repr(value), 1126 safe_repr(dictionary[key]))) 1127 1128 if not (missing or mismatched): 1129 return 1130 1131 standardMsg = '' 1132 if missing: 1133 standardMsg = 'Missing: %s' % ','.join(safe_repr(m) for m in 1134 missing) 1135 if mismatched: 1136 if standardMsg: 1137 standardMsg += '; ' 1138 standardMsg += 'Mismatched values: %s' % ','.join(mismatched) 1139 1140 self.fail(self._formatMessage(msg, standardMsg)) 1141 1142 1143 def assertCountEqual(self, first, second, msg=None): 1144 """An unordered sequence comparison asserting that the same elements, 1145 regardless of order. If the same element occurs more than once, 1146 it verifies that the elements occur the same number of times. 1147 1148 self.assertEqual(Counter(list(first)), 1149 Counter(list(second))) 1150 1151 Example: 1152 - [0, 1, 1] and [1, 0, 1] compare equal. 1153 - [0, 0, 1] and [0, 1] compare unequal. 1154 1155 """ 1156 first_seq, second_seq = list(first), list(second) 1157 try: 1158 first = collections.Counter(first_seq) 1159 second = collections.Counter(second_seq) 1160 except TypeError: 1161 # Handle case with unhashable elements 1162 differences = _count_diff_all_purpose(first_seq, second_seq) 1163 else: 1164 if first == second: 1165 return 1166 differences = _count_diff_hashable(first_seq, second_seq) 1167 1168 if differences: 1169 standardMsg = 'Element counts were not equal:\n' 1170 lines = ['First has %d, Second has %d: %r' % diff for diff in differences] 1171 diffMsg = '\n'.join(lines) 1172 standardMsg = self._truncateMessage(standardMsg, diffMsg) 1173 msg = self._formatMessage(msg, standardMsg) 1174 self.fail(msg) 1175 1176 def assertMultiLineEqual(self, first, second, msg=None): 1177 """Assert that two multi-line strings are equal.""" 1178 self.assertIsInstance(first, str, 'First argument is not a string') 1179 self.assertIsInstance(second, str, 'Second argument is not a string') 1180 1181 if first != second: 1182 # don't use difflib if the strings are too long 1183 if (len(first) > self._diffThreshold or 1184 len(second) > self._diffThreshold): 1185 self._baseAssertEqual(first, second, msg) 1186 firstlines = first.splitlines(keepends=True) 1187 secondlines = second.splitlines(keepends=True) 1188 if len(firstlines) == 1 and first.strip('\r\n') == first: 1189 firstlines = [first + '\n'] 1190 secondlines = [second + '\n'] 1191 standardMsg = '%s != %s' % _common_shorten_repr(first, second) 1192 diff = '\n' + ''.join(difflib.ndiff(firstlines, secondlines)) 1193 standardMsg = self._truncateMessage(standardMsg, diff) 1194 self.fail(self._formatMessage(msg, standardMsg)) 1195 1196 def assertLess(self, a, b, msg=None): 1197 """Just like self.assertTrue(a < b), but with a nicer default message.""" 1198 if not a < b: 1199 standardMsg = '%s not less than %s' % (safe_repr(a), safe_repr(b)) 1200 self.fail(self._formatMessage(msg, standardMsg)) 1201 1202 def assertLessEqual(self, a, b, msg=None): 1203 """Just like self.assertTrue(a <= b), but with a nicer default message.""" 1204 if not a <= b: 1205 standardMsg = '%s not less than or equal to %s' % (safe_repr(a), safe_repr(b)) 1206 self.fail(self._formatMessage(msg, standardMsg)) 1207 1208 def assertGreater(self, a, b, msg=None): 1209 """Just like self.assertTrue(a > b), but with a nicer default message.""" 1210 if not a > b: 1211 standardMsg = '%s not greater than %s' % (safe_repr(a), safe_repr(b)) 1212 self.fail(self._formatMessage(msg, standardMsg)) 1213 1214 def assertGreaterEqual(self, a, b, msg=None): 1215 """Just like self.assertTrue(a >= b), but with a nicer default message.""" 1216 if not a >= b: 1217 standardMsg = '%s not greater than or equal to %s' % (safe_repr(a), safe_repr(b)) 1218 self.fail(self._formatMessage(msg, standardMsg)) 1219 1220 def assertIsNone(self, obj, msg=None): 1221 """Same as self.assertTrue(obj is None), with a nicer default message.""" 1222 if obj is not None: 1223 standardMsg = '%s is not None' % (safe_repr(obj),) 1224 self.fail(self._formatMessage(msg, standardMsg)) 1225 1226 def assertIsNotNone(self, obj, msg=None): 1227 """Included for symmetry with assertIsNone.""" 1228 if obj is None: 1229 standardMsg = 'unexpectedly None' 1230 self.fail(self._formatMessage(msg, standardMsg)) 1231 1232 def assertIsInstance(self, obj, cls, msg=None): 1233 """Same as self.assertTrue(isinstance(obj, cls)), with a nicer 1234 default message.""" 1235 if not isinstance(obj, cls): 1236 standardMsg = '%s is not an instance of %r' % (safe_repr(obj), cls) 1237 self.fail(self._formatMessage(msg, standardMsg)) 1238 1239 def assertNotIsInstance(self, obj, cls, msg=None): 1240 """Included for symmetry with assertIsInstance.""" 1241 if isinstance(obj, cls): 1242 standardMsg = '%s is an instance of %r' % (safe_repr(obj), cls) 1243 self.fail(self._formatMessage(msg, standardMsg)) 1244 1245 def assertRaisesRegex(self, expected_exception, expected_regex, 1246 *args, **kwargs): 1247 """Asserts that the message in a raised exception matches a regex. 1248 1249 Args: 1250 expected_exception: Exception class expected to be raised. 1251 expected_regex: Regex (re pattern object or string) expected 1252 to be found in error message. 1253 args: Function to be called and extra positional args. 1254 kwargs: Extra kwargs. 1255 msg: Optional message used in case of failure. Can only be used 1256 when assertRaisesRegex is used as a context manager. 1257 """ 1258 context = _AssertRaisesContext(expected_exception, self, expected_regex) 1259 return context.handle('assertRaisesRegex', args, kwargs) 1260 1261 def assertWarnsRegex(self, expected_warning, expected_regex, 1262 *args, **kwargs): 1263 """Asserts that the message in a triggered warning matches a regexp. 1264 Basic functioning is similar to assertWarns() with the addition 1265 that only warnings whose messages also match the regular expression 1266 are considered successful matches. 1267 1268 Args: 1269 expected_warning: Warning class expected to be triggered. 1270 expected_regex: Regex (re pattern object or string) expected 1271 to be found in error message. 1272 args: Function to be called and extra positional args. 1273 kwargs: Extra kwargs. 1274 msg: Optional message used in case of failure. Can only be used 1275 when assertWarnsRegex is used as a context manager. 1276 """ 1277 context = _AssertWarnsContext(expected_warning, self, expected_regex) 1278 return context.handle('assertWarnsRegex', args, kwargs) 1279 1280 def assertRegex(self, text, expected_regex, msg=None): 1281 """Fail the test unless the text matches the regular expression.""" 1282 if isinstance(expected_regex, (str, bytes)): 1283 assert expected_regex, "expected_regex must not be empty." 1284 expected_regex = re.compile(expected_regex) 1285 if not expected_regex.search(text): 1286 standardMsg = "Regex didn't match: %r not found in %r" % ( 1287 expected_regex.pattern, text) 1288 # _formatMessage ensures the longMessage option is respected 1289 msg = self._formatMessage(msg, standardMsg) 1290 raise self.failureException(msg) 1291 1292 def assertNotRegex(self, text, unexpected_regex, msg=None): 1293 """Fail the test if the text matches the regular expression.""" 1294 if isinstance(unexpected_regex, (str, bytes)): 1295 unexpected_regex = re.compile(unexpected_regex) 1296 match = unexpected_regex.search(text) 1297 if match: 1298 standardMsg = 'Regex matched: %r matches %r in %r' % ( 1299 text[match.start() : match.end()], 1300 unexpected_regex.pattern, 1301 text) 1302 # _formatMessage ensures the longMessage option is respected 1303 msg = self._formatMessage(msg, standardMsg) 1304 raise self.failureException(msg) 1305 1306 1307 def _deprecate(original_func): 1308 def deprecated_func(*args, **kwargs): 1309 warnings.warn( 1310 'Please use {0} instead.'.format(original_func.__name__), 1311 DeprecationWarning, 2) 1312 return original_func(*args, **kwargs) 1313 return deprecated_func 1314 1315 # see #9424 1316 failUnlessEqual = assertEquals = _deprecate(assertEqual) 1317 failIfEqual = assertNotEquals = _deprecate(assertNotEqual) 1318 failUnlessAlmostEqual = assertAlmostEquals = _deprecate(assertAlmostEqual) 1319 failIfAlmostEqual = assertNotAlmostEquals = _deprecate(assertNotAlmostEqual) 1320 failUnless = assert_ = _deprecate(assertTrue) 1321 failUnlessRaises = _deprecate(assertRaises) 1322 failIf = _deprecate(assertFalse) 1323 assertRaisesRegexp = _deprecate(assertRaisesRegex) 1324 assertRegexpMatches = _deprecate(assertRegex) 1325 assertNotRegexpMatches = _deprecate(assertNotRegex) 1326 1327 1328 1329class FunctionTestCase(TestCase): 1330 """A test case that wraps a test function. 1331 1332 This is useful for slipping pre-existing test functions into the 1333 unittest framework. Optionally, set-up and tidy-up functions can be 1334 supplied. As with TestCase, the tidy-up ('tearDown') function will 1335 always be called if the set-up ('setUp') function ran successfully. 1336 """ 1337 1338 def __init__(self, testFunc, setUp=None, tearDown=None, description=None): 1339 super(FunctionTestCase, self).__init__() 1340 self._setUpFunc = setUp 1341 self._tearDownFunc = tearDown 1342 self._testFunc = testFunc 1343 self._description = description 1344 1345 def setUp(self): 1346 if self._setUpFunc is not None: 1347 self._setUpFunc() 1348 1349 def tearDown(self): 1350 if self._tearDownFunc is not None: 1351 self._tearDownFunc() 1352 1353 def runTest(self): 1354 self._testFunc() 1355 1356 def id(self): 1357 return self._testFunc.__name__ 1358 1359 def __eq__(self, other): 1360 if not isinstance(other, self.__class__): 1361 return NotImplemented 1362 1363 return self._setUpFunc == other._setUpFunc and \ 1364 self._tearDownFunc == other._tearDownFunc and \ 1365 self._testFunc == other._testFunc and \ 1366 self._description == other._description 1367 1368 def __hash__(self): 1369 return hash((type(self), self._setUpFunc, self._tearDownFunc, 1370 self._testFunc, self._description)) 1371 1372 def __str__(self): 1373 return "%s (%s)" % (strclass(self.__class__), 1374 self._testFunc.__name__) 1375 1376 def __repr__(self): 1377 return "<%s tec=%s>" % (strclass(self.__class__), 1378 self._testFunc) 1379 1380 def shortDescription(self): 1381 if self._description is not None: 1382 return self._description 1383 doc = self._testFunc.__doc__ 1384 return doc and doc.split("\n")[0].strip() or None 1385 1386 1387class _SubTest(TestCase): 1388 1389 def __init__(self, test_case, message, params): 1390 super().__init__() 1391 self._message = message 1392 self.test_case = test_case 1393 self.params = params 1394 self.failureException = test_case.failureException 1395 1396 def runTest(self): 1397 raise NotImplementedError("subtests cannot be run directly") 1398 1399 def _subDescription(self): 1400 parts = [] 1401 if self._message is not _subtest_msg_sentinel: 1402 parts.append("[{}]".format(self._message)) 1403 if self.params: 1404 params_desc = ', '.join( 1405 "{}={!r}".format(k, v) 1406 for (k, v) in sorted(self.params.items())) 1407 parts.append("({})".format(params_desc)) 1408 return " ".join(parts) or '(<subtest>)' 1409 1410 def id(self): 1411 return "{} {}".format(self.test_case.id(), self._subDescription()) 1412 1413 def shortDescription(self): 1414 """Returns a one-line description of the subtest, or None if no 1415 description has been provided. 1416 """ 1417 return self.test_case.shortDescription() 1418 1419 def __str__(self): 1420 return "{} {}".format(self.test_case, self._subDescription()) 1421