1# Copyright 2017 The Abseil Authors. 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); 4# you may not use this file except in compliance with the License. 5# You may obtain a copy of the License at 6# 7# http://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12# See the License for the specific language governing permissions and 13# limitations under the License. 14 15"""Tests for absl.testing.parameterized.""" 16 17from collections import abc 18import os 19import sys 20import unittest 21 22from absl.testing import absltest 23from absl.testing import parameterized 24 25 26class MyOwnClass(object): 27 pass 28 29 30def dummy_decorator(method): 31 def decorated(*args, **kwargs): 32 return method(*args, **kwargs) 33 34 return decorated 35 36 37def dict_decorator(key, value): 38 """Sample implementation of a chained decorator. 39 40 Sets a single field in a dict on a test with a dict parameter. 41 Uses the exposed '_ParameterizedTestIter.testcases' field to 42 modify arguments from previous decorators to allow decorator chains. 43 44 Args: 45 key: key to map to 46 value: value to set 47 48 Returns: 49 The test decorator 50 """ 51 52 def decorator(test_method): 53 # If decorating result of another dict_decorator 54 if isinstance(test_method, abc.Iterable): 55 actual_tests = [] 56 for old_test in test_method.testcases: 57 # each test is a ('test_suffix', dict) tuple 58 new_dict = old_test[1].copy() 59 new_dict[key] = value 60 test_suffix = '%s_%s_%s' % (old_test[0], key, value) 61 actual_tests.append((test_suffix, new_dict)) 62 63 test_method.testcases = actual_tests 64 return test_method 65 else: 66 test_suffix = '_%s_%s' % (key, value) 67 tests_to_make = ((test_suffix, {key: value}),) 68 # 'test_method' here is the original test method 69 return parameterized.named_parameters(*tests_to_make)(test_method) 70 71 return decorator 72 73 74class ParameterizedTestsTest(absltest.TestCase): 75 # The test testcases are nested so they're not 76 # picked up by the normal test case loader code. 77 78 class GoodAdditionParams(parameterized.TestCase): 79 80 @parameterized.parameters((1, 2, 3), (4, 5, 9)) 81 def test_addition(self, op1, op2, result): 82 self.arguments = (op1, op2, result) 83 self.assertEqual(result, op1 + op2) 84 85 # This class does not inherit from TestCase. 86 class BadAdditionParams(absltest.TestCase): 87 88 @parameterized.parameters((1, 2, 3), (4, 5, 9)) 89 def test_addition(self, op1, op2, result): 90 pass # Always passes, but not called w/out TestCase. 91 92 class MixedAdditionParams(parameterized.TestCase): 93 94 @parameterized.parameters((1, 2, 1), (4, 5, 9)) 95 def test_addition(self, op1, op2, result): 96 self.arguments = (op1, op2, result) 97 self.assertEqual(result, op1 + op2) 98 99 class DictionaryArguments(parameterized.TestCase): 100 101 @parameterized.parameters( 102 {'op1': 1, 'op2': 2, 'result': 3}, {'op1': 4, 'op2': 5, 'result': 9} 103 ) 104 def test_addition(self, op1, op2, result): 105 self.assertEqual(result, op1 + op2) 106 107 class NoParameterizedTests(parameterized.TestCase): 108 # iterable member with non-matching name 109 a = 'BCD' 110 # member with matching name, but not a generator 111 testInstanceMember = None # pylint: disable=invalid-name 112 test_instance_member = None 113 114 # member with a matching name and iterator, but not a generator 115 testString = 'foo' # pylint: disable=invalid-name 116 test_string = 'foo' 117 118 # generator, but no matching name 119 def someGenerator(self): # pylint: disable=invalid-name 120 yield 121 yield 122 yield 123 124 def some_generator(self): 125 yield 126 yield 127 yield 128 129 # Generator function, but not a generator instance. 130 def testGenerator(self): 131 yield 132 yield 133 yield 134 135 def test_generator(self): 136 yield 137 yield 138 yield 139 140 def testNormal(self): 141 self.assertEqual(3, 1 + 2) 142 143 def test_normal(self): 144 self.assertEqual(3, 1 + 2) 145 146 class ArgumentsWithAddresses(parameterized.TestCase): 147 148 @parameterized.parameters( 149 (object(),), 150 (MyOwnClass(),), 151 ) 152 def test_something(self, case): 153 pass 154 155 class CamelCaseNamedTests(parameterized.TestCase): 156 157 @parameterized.named_parameters( 158 ('Interesting', 0), 159 ) 160 def testSingle(self, case): 161 pass 162 163 @parameterized.named_parameters( 164 {'testcase_name': 'Interesting', 'case': 0}, 165 ) 166 def testDictSingle(self, case): 167 pass 168 169 @parameterized.named_parameters( 170 ('Interesting', 0), 171 ('Boring', 1), 172 ) 173 def testSomething(self, case): 174 pass 175 176 @parameterized.named_parameters( 177 {'testcase_name': 'Interesting', 'case': 0}, 178 {'testcase_name': 'Boring', 'case': 1}, 179 ) 180 def testDictSomething(self, case): 181 pass 182 183 @parameterized.named_parameters( 184 {'testcase_name': 'Interesting', 'case': 0}, 185 ('Boring', 1), 186 ) 187 def testMixedSomething(self, case): 188 pass 189 190 def testWithoutParameters(self): 191 pass 192 193 class NamedTests(parameterized.TestCase): 194 """Example tests using PEP-8 style names instead of camel-case.""" 195 196 @parameterized.named_parameters( 197 ('interesting', 0), 198 ) 199 def test_single(self, case): 200 pass 201 202 @parameterized.named_parameters( 203 {'testcase_name': 'interesting', 'case': 0}, 204 ) 205 def test_dict_single(self, case): 206 pass 207 208 @parameterized.named_parameters( 209 ('interesting', 0), 210 ('boring', 1), 211 ) 212 def test_something(self, case): 213 pass 214 215 @parameterized.named_parameters( 216 {'testcase_name': 'interesting', 'case': 0}, 217 {'testcase_name': 'boring', 'case': 1}, 218 ) 219 def test_dict_something(self, case): 220 pass 221 222 @parameterized.named_parameters( 223 {'testcase_name': 'interesting', 'case': 0}, 224 ('boring', 1), 225 ) 226 def test_mixed_something(self, case): 227 pass 228 229 def test_without_parameters(self): 230 pass 231 232 class ChainedTests(parameterized.TestCase): 233 234 @dict_decorator('cone', 'waffle') 235 @dict_decorator('flavor', 'strawberry') 236 def test_chained(self, dictionary): 237 self.assertDictEqual( 238 dictionary, {'cone': 'waffle', 'flavor': 'strawberry'} 239 ) 240 241 class SingletonListExtraction(parameterized.TestCase): 242 243 @parameterized.parameters((i, i * 2) for i in range(10)) 244 def test_something(self, unused_1, unused_2): 245 pass 246 247 class SingletonArgumentExtraction(parameterized.TestCase): 248 249 @parameterized.parameters(1, 2, 3, 4, 5, 6) 250 def test_numbers(self, unused_1): 251 pass 252 253 @parameterized.parameters('foo', 'bar', 'baz') 254 def test_strings(self, unused_1): 255 pass 256 257 class SingletonDictArgument(parameterized.TestCase): 258 259 @parameterized.parameters({'op1': 1, 'op2': 2}) 260 def test_something(self, op1, op2): 261 del op1, op2 262 263 @parameterized.parameters((1, 2, 3), (4, 5, 9)) 264 class DecoratedClass(parameterized.TestCase): 265 266 def test_add(self, arg1, arg2, arg3): 267 self.assertEqual(arg1 + arg2, arg3) 268 269 def test_subtract_fail(self, arg1, arg2, arg3): 270 self.assertEqual(arg3 + arg2, arg1) 271 272 @parameterized.parameters( 273 (a, b, a + b) for a in range(1, 5) for b in range(1, 5) 274 ) 275 class GeneratorDecoratedClass(parameterized.TestCase): 276 277 def test_add(self, arg1, arg2, arg3): 278 self.assertEqual(arg1 + arg2, arg3) 279 280 def test_subtract_fail(self, arg1, arg2, arg3): 281 self.assertEqual(arg3 + arg2, arg1) 282 283 @parameterized.parameters( 284 (1, 2, 3), 285 (4, 5, 9), 286 ) 287 class DecoratedBareClass(absltest.TestCase): 288 289 def test_add(self, arg1, arg2, arg3): 290 self.assertEqual(arg1 + arg2, arg3) 291 292 class OtherDecoratorUnnamed(parameterized.TestCase): 293 294 @dummy_decorator 295 @parameterized.parameters((1), (2)) 296 def test_other_then_parameterized(self, arg1): 297 pass 298 299 @parameterized.parameters((1), (2)) 300 @dummy_decorator 301 def test_parameterized_then_other(self, arg1): 302 pass 303 304 class OtherDecoratorNamed(parameterized.TestCase): 305 306 @dummy_decorator 307 @parameterized.named_parameters(('a', 1), ('b', 2)) 308 def test_other_then_parameterized(self, arg1): 309 pass 310 311 @parameterized.named_parameters(('a', 1), ('b', 2)) 312 @dummy_decorator 313 def test_parameterized_then_other(self, arg1): 314 pass 315 316 class OtherDecoratorNamedWithDict(parameterized.TestCase): 317 318 @dummy_decorator 319 @parameterized.named_parameters( 320 {'testcase_name': 'a', 'arg1': 1}, {'testcase_name': 'b', 'arg1': 2} 321 ) 322 def test_other_then_parameterized(self, arg1): 323 pass 324 325 @parameterized.named_parameters( 326 {'testcase_name': 'a', 'arg1': 1}, {'testcase_name': 'b', 'arg1': 2} 327 ) 328 @dummy_decorator 329 def test_parameterized_then_other(self, arg1): 330 pass 331 332 class UniqueDescriptiveNamesTest(parameterized.TestCase): 333 334 @parameterized.parameters(13, 13) 335 def test_normal(self, number): 336 del number 337 338 class MultiGeneratorsTestCase(parameterized.TestCase): 339 340 @parameterized.parameters((i for i in (1, 2, 3)), (i for i in (3, 2, 1))) 341 def test_sum(self, a, b, c): 342 self.assertEqual(6, sum([a, b, c])) 343 344 class NamedParametersReusableTestCase(parameterized.TestCase): 345 named_params_a = ( 346 {'testcase_name': 'dict_a', 'unused_obj': 0}, 347 ('list_a', 1), 348 ) 349 named_params_b = ( 350 {'testcase_name': 'dict_b', 'unused_obj': 2}, 351 ('list_b', 3), 352 ) 353 named_params_c = ( 354 {'testcase_name': 'dict_c', 'unused_obj': 4}, 355 ('list_b', 5), 356 ) 357 358 @parameterized.named_parameters(*(named_params_a + named_params_b)) 359 def testSomething(self, unused_obj): 360 pass 361 362 @parameterized.named_parameters(*(named_params_a + named_params_c)) 363 def testSomethingElse(self, unused_obj): 364 pass 365 366 class SuperclassTestCase(parameterized.TestCase): 367 368 @parameterized.parameters('foo', 'bar') 369 def test_name(self, name): 370 del name 371 372 class SubclassTestCase(SuperclassTestCase): 373 pass 374 375 @unittest.skipIf( 376 (sys.version_info[:2] == (3, 7) and sys.version_info[2] in {0, 1, 2}), 377 'Python 3.7.0 to 3.7.2 have a bug that breaks this test, see ' 378 'https://bugs.python.org/issue35767', 379 ) 380 def test_missing_inheritance(self): 381 ts = unittest.makeSuite(self.BadAdditionParams) 382 self.assertEqual(1, ts.countTestCases()) 383 384 res = unittest.TestResult() 385 ts.run(res) 386 self.assertEqual(1, res.testsRun) 387 self.assertFalse(res.wasSuccessful()) 388 self.assertIn('without having inherited', str(res.errors[0])) 389 390 def test_correct_extraction_numbers(self): 391 ts = unittest.makeSuite(self.GoodAdditionParams) 392 self.assertEqual(2, ts.countTestCases()) 393 394 def test_successful_execution(self): 395 ts = unittest.makeSuite(self.GoodAdditionParams) 396 397 res = unittest.TestResult() 398 ts.run(res) 399 self.assertEqual(2, res.testsRun) 400 self.assertTrue(res.wasSuccessful()) 401 402 def test_correct_arguments(self): 403 ts = unittest.makeSuite(self.GoodAdditionParams) 404 res = unittest.TestResult() 405 406 params = set([(1, 2, 3), (4, 5, 9)]) 407 for test in ts: 408 test(res) 409 self.assertIn(test.arguments, params) 410 params.remove(test.arguments) 411 self.assertEmpty(params) 412 413 def test_recorded_failures(self): 414 ts = unittest.makeSuite(self.MixedAdditionParams) 415 self.assertEqual(2, ts.countTestCases()) 416 417 res = unittest.TestResult() 418 ts.run(res) 419 self.assertEqual(2, res.testsRun) 420 self.assertFalse(res.wasSuccessful()) 421 self.assertLen(res.failures, 1) 422 self.assertEmpty(res.errors) 423 424 def test_short_description(self): 425 ts = unittest.makeSuite(self.GoodAdditionParams) 426 short_desc = list(ts)[0].shortDescription() 427 428 location = unittest.util.strclass(self.GoodAdditionParams).replace( 429 '__main__.', '' 430 ) 431 expected = ( 432 '{}.test_addition0 (1, 2, 3)\n'.format(location) 433 + 'test_addition(1, 2, 3)' 434 ) 435 self.assertEqual(expected, short_desc) 436 437 def test_short_description_addresses_removed(self): 438 ts = unittest.makeSuite(self.ArgumentsWithAddresses) 439 short_desc = list(ts)[0].shortDescription().split('\n') 440 self.assertEqual('test_something(<object>)', short_desc[1]) 441 short_desc = list(ts)[1].shortDescription().split('\n') 442 self.assertEqual('test_something(<__main__.MyOwnClass>)', short_desc[1]) 443 444 def test_id(self): 445 ts = unittest.makeSuite(self.ArgumentsWithAddresses) 446 self.assertEqual( 447 ( 448 unittest.util.strclass(self.ArgumentsWithAddresses) 449 + '.test_something0 (<object>)' 450 ), 451 list(ts)[0].id(), 452 ) 453 ts = unittest.makeSuite(self.GoodAdditionParams) 454 self.assertEqual( 455 ( 456 unittest.util.strclass(self.GoodAdditionParams) 457 + '.test_addition0 (1, 2, 3)' 458 ), 459 list(ts)[0].id(), 460 ) 461 462 def test_str(self): 463 ts = unittest.makeSuite(self.GoodAdditionParams) 464 test = list(ts)[0] 465 466 expected = 'test_addition0 (1, 2, 3) ({})'.format( 467 unittest.util.strclass(self.GoodAdditionParams) 468 ) 469 self.assertEqual(expected, str(test)) 470 471 def test_dict_parameters(self): 472 ts = unittest.makeSuite(self.DictionaryArguments) 473 res = unittest.TestResult() 474 ts.run(res) 475 self.assertEqual(2, res.testsRun) 476 self.assertTrue(res.wasSuccessful()) 477 478 def test_no_parameterized_tests(self): 479 ts = unittest.makeSuite(self.NoParameterizedTests) 480 self.assertEqual(4, ts.countTestCases()) 481 short_descs = [x.shortDescription() for x in list(ts)] 482 full_class_name = unittest.util.strclass(self.NoParameterizedTests) 483 full_class_name = full_class_name.replace('__main__.', '') 484 self.assertSameElements( 485 [ 486 '{}.testGenerator'.format(full_class_name), 487 '{}.test_generator'.format(full_class_name), 488 '{}.testNormal'.format(full_class_name), 489 '{}.test_normal'.format(full_class_name), 490 ], 491 short_descs, 492 ) 493 494 def test_successful_product_test_testgrid(self): 495 class GoodProductTestCase(parameterized.TestCase): 496 497 @parameterized.product(num=(0, 20, 80), modulo=(2, 4), expected=(0,)) 498 def testModuloResult(self, num, modulo, expected): 499 self.assertEqual(expected, num % modulo) 500 501 ts = unittest.makeSuite(GoodProductTestCase) 502 res = unittest.TestResult() 503 ts.run(res) 504 self.assertEqual(ts.countTestCases(), 6) 505 self.assertEqual(res.testsRun, 6) 506 self.assertTrue(res.wasSuccessful()) 507 508 def test_successful_product_test_kwarg_seqs(self): 509 class GoodProductTestCase(parameterized.TestCase): 510 511 @parameterized.product( 512 (dict(num=0), dict(num=20), dict(num=0)), 513 (dict(modulo=2), dict(modulo=4)), 514 (dict(expected=0),), 515 ) 516 def testModuloResult(self, num, modulo, expected): 517 self.assertEqual(expected, num % modulo) 518 519 ts = unittest.makeSuite(GoodProductTestCase) 520 res = unittest.TestResult() 521 ts.run(res) 522 self.assertEqual(ts.countTestCases(), 6) 523 self.assertEqual(res.testsRun, 6) 524 self.assertTrue(res.wasSuccessful()) 525 526 def test_successful_product_test_kwarg_seq_and_testgrid(self): 527 class GoodProductTestCase(parameterized.TestCase): 528 529 @parameterized.product( 530 ( 531 dict(num=5, modulo=3, expected=2), 532 dict(num=7, modulo=4, expected=3), 533 ), 534 dtype=(int, float), 535 ) 536 def testModuloResult(self, num, dtype, modulo, expected): 537 self.assertEqual(expected, dtype(num) % modulo) 538 539 ts = unittest.makeSuite(GoodProductTestCase) 540 res = unittest.TestResult() 541 ts.run(res) 542 self.assertEqual(ts.countTestCases(), 4) 543 self.assertEqual(res.testsRun, 4) 544 self.assertTrue(res.wasSuccessful()) 545 546 def test_inconsistent_arg_names_in_kwargs_seq(self): 547 with self.assertRaisesRegex(AssertionError, 'must all have the same keys'): 548 549 class BadProductParams(parameterized.TestCase): # pylint: disable=unused-variable 550 551 @parameterized.product( 552 (dict(num=5, modulo=3), dict(num=7, modula=2)), dtype=(int, float) 553 ) 554 def test_something(self): 555 pass # not called because argnames are not the same 556 557 def test_duplicate_arg_names_in_kwargs_seqs(self): 558 with self.assertRaisesRegex(AssertionError, 'must all have distinct'): 559 560 class BadProductParams(parameterized.TestCase): # pylint: disable=unused-variable 561 562 @parameterized.product( 563 (dict(num=5, modulo=3), dict(num=7, modulo=4)), 564 (dict(foo='bar', num=5), dict(foo='baz', num=7)), 565 dtype=(int, float), 566 ) 567 def test_something(self): 568 pass # not called because `num` is specified twice 569 570 def test_duplicate_arg_names_in_kwargs_seq_and_testgrid(self): 571 with self.assertRaisesRegex(AssertionError, 'duplicate argument'): 572 573 class BadProductParams(parameterized.TestCase): # pylint: disable=unused-variable 574 575 @parameterized.product( 576 (dict(num=5, modulo=3), dict(num=7, modulo=4)), 577 (dict(foo='bar'), dict(foo='baz')), 578 dtype=(int, float), 579 foo=('a', 'b'), 580 ) 581 def test_something(self): 582 pass # not called because `foo` is specified twice 583 584 def test_product_recorded_failures(self): 585 class MixedProductTestCase(parameterized.TestCase): 586 587 @parameterized.product(num=(0, 10, 20), modulo=(2, 4), expected=(0,)) 588 def testModuloResult(self, num, modulo, expected): 589 self.assertEqual(expected, num % modulo) 590 591 ts = unittest.makeSuite(MixedProductTestCase) 592 self.assertEqual(6, ts.countTestCases()) 593 594 res = unittest.TestResult() 595 ts.run(res) 596 self.assertEqual(res.testsRun, 6) 597 self.assertFalse(res.wasSuccessful()) 598 self.assertLen(res.failures, 1) 599 self.assertEmpty(res.errors) 600 601 def test_mismatched_product_parameter(self): 602 class MismatchedProductParam(parameterized.TestCase): 603 604 @parameterized.product(a=(1, 2), mismatch=(1, 2)) 605 # will fail because of mismatch in parameter names. 606 def test_something(self, a, b): 607 pass 608 609 ts = unittest.makeSuite(MismatchedProductParam) 610 res = unittest.TestResult() 611 ts.run(res) 612 self.assertEqual(res.testsRun, 4) 613 self.assertFalse(res.wasSuccessful()) 614 self.assertLen(res.errors, 4) 615 616 def test_no_test_error_empty_product_parameter(self): 617 with self.assertRaises(parameterized.NoTestsError): 618 619 class EmptyProductParam(parameterized.TestCase): # pylint: disable=unused-variable 620 621 @parameterized.product(arg1=[1, 2], arg2=[]) 622 def test_something(self, arg1, arg2): 623 pass # not called because arg2 has empty list of values. 624 625 def test_bad_product_parameters(self): 626 with self.assertRaisesRegex(AssertionError, 'must be given as list or'): 627 628 class BadProductParams(parameterized.TestCase): # pylint: disable=unused-variable 629 630 @parameterized.product(arg1=[1, 2], arg2='abcd') 631 def test_something(self, arg1, arg2): 632 pass # not called because arg2 is not list or tuple. 633 634 def test_generator_tests_disallowed(self): 635 with self.assertRaisesRegex(RuntimeError, 'generated.*without'): 636 637 class GeneratorTests(parameterized.TestCase): # pylint: disable=unused-variable 638 test_generator_method = (lambda self: None for _ in range(10)) 639 640 def test_named_parameters_run(self): 641 ts = unittest.makeSuite(self.NamedTests) 642 self.assertEqual(9, ts.countTestCases()) 643 res = unittest.TestResult() 644 ts.run(res) 645 self.assertEqual(9, res.testsRun) 646 self.assertTrue(res.wasSuccessful()) 647 648 def test_named_parameters_id(self): 649 ts = sorted( 650 unittest.makeSuite(self.CamelCaseNamedTests), key=lambda t: t.id() 651 ) 652 self.assertLen(ts, 9) 653 full_class_name = unittest.util.strclass(self.CamelCaseNamedTests) 654 self.assertEqual(full_class_name + '.testDictSingleInteresting', ts[0].id()) 655 self.assertEqual(full_class_name + '.testDictSomethingBoring', ts[1].id()) 656 self.assertEqual( 657 full_class_name + '.testDictSomethingInteresting', ts[2].id() 658 ) 659 self.assertEqual(full_class_name + '.testMixedSomethingBoring', ts[3].id()) 660 self.assertEqual( 661 full_class_name + '.testMixedSomethingInteresting', ts[4].id() 662 ) 663 self.assertEqual(full_class_name + '.testSingleInteresting', ts[5].id()) 664 self.assertEqual(full_class_name + '.testSomethingBoring', ts[6].id()) 665 self.assertEqual(full_class_name + '.testSomethingInteresting', ts[7].id()) 666 self.assertEqual(full_class_name + '.testWithoutParameters', ts[8].id()) 667 668 def test_named_parameters_id_with_underscore_case(self): 669 ts = sorted(unittest.makeSuite(self.NamedTests), key=lambda t: t.id()) 670 self.assertLen(ts, 9) 671 full_class_name = unittest.util.strclass(self.NamedTests) 672 self.assertEqual( 673 full_class_name + '.test_dict_single_interesting', ts[0].id() 674 ) 675 self.assertEqual( 676 full_class_name + '.test_dict_something_boring', ts[1].id() 677 ) 678 self.assertEqual( 679 full_class_name + '.test_dict_something_interesting', ts[2].id() 680 ) 681 self.assertEqual( 682 full_class_name + '.test_mixed_something_boring', ts[3].id() 683 ) 684 self.assertEqual( 685 full_class_name + '.test_mixed_something_interesting', ts[4].id() 686 ) 687 self.assertEqual(full_class_name + '.test_single_interesting', ts[5].id()) 688 self.assertEqual(full_class_name + '.test_something_boring', ts[6].id()) 689 self.assertEqual( 690 full_class_name + '.test_something_interesting', ts[7].id() 691 ) 692 self.assertEqual(full_class_name + '.test_without_parameters', ts[8].id()) 693 694 def test_named_parameters_short_description(self): 695 ts = sorted(unittest.makeSuite(self.NamedTests), key=lambda t: t.id()) 696 actual = {t._testMethodName: t.shortDescription() for t in ts} 697 expected = { 698 'test_dict_single_interesting': 'case=0', 699 'test_dict_something_boring': 'case=1', 700 'test_dict_something_interesting': 'case=0', 701 'test_mixed_something_boring': '1', 702 'test_mixed_something_interesting': 'case=0', 703 'test_something_boring': '1', 704 'test_something_interesting': '0', 705 } 706 for test_name, param_repr in expected.items(): 707 short_desc = actual[test_name].split('\n') 708 self.assertIn(test_name, short_desc[0]) 709 self.assertEqual('{}({})'.format(test_name, param_repr), short_desc[1]) 710 711 def test_load_tuple_named_test(self): 712 loader = unittest.TestLoader() 713 ts = list( 714 loader.loadTestsFromName( 715 'NamedTests.test_something_interesting', module=self 716 ) 717 ) 718 self.assertLen(ts, 1) 719 self.assertEndsWith(ts[0].id(), '.test_something_interesting') 720 721 def test_load_dict_named_test(self): 722 loader = unittest.TestLoader() 723 ts = list( 724 loader.loadTestsFromName( 725 'NamedTests.test_dict_something_interesting', module=self 726 ) 727 ) 728 self.assertLen(ts, 1) 729 self.assertEndsWith(ts[0].id(), '.test_dict_something_interesting') 730 731 def test_load_mixed_named_test(self): 732 loader = unittest.TestLoader() 733 ts = list( 734 loader.loadTestsFromName( 735 'NamedTests.test_mixed_something_interesting', module=self 736 ) 737 ) 738 self.assertLen(ts, 1) 739 self.assertEndsWith(ts[0].id(), '.test_mixed_something_interesting') 740 741 def test_duplicate_named_test_fails(self): 742 with self.assertRaises(parameterized.DuplicateTestNameError): 743 744 class _(parameterized.TestCase): 745 746 @parameterized.named_parameters( 747 ('Interesting', 0), 748 ('Interesting', 1), 749 ) 750 def test_something(self, unused_obj): 751 pass 752 753 def test_duplicate_dict_named_test_fails(self): 754 with self.assertRaises(parameterized.DuplicateTestNameError): 755 756 class _(parameterized.TestCase): 757 758 @parameterized.named_parameters( 759 {'testcase_name': 'Interesting', 'unused_obj': 0}, 760 {'testcase_name': 'Interesting', 'unused_obj': 1}, 761 ) 762 def test_dict_something(self, unused_obj): 763 pass 764 765 def test_duplicate_mixed_named_test_fails(self): 766 with self.assertRaises(parameterized.DuplicateTestNameError): 767 768 class _(parameterized.TestCase): 769 770 @parameterized.named_parameters( 771 {'testcase_name': 'Interesting', 'unused_obj': 0}, 772 ('Interesting', 1), 773 ) 774 def test_mixed_something(self, unused_obj): 775 pass 776 777 def test_named_test_with_no_name_fails(self): 778 with self.assertRaises(RuntimeError): 779 780 class _(parameterized.TestCase): 781 782 @parameterized.named_parameters( 783 (0,), 784 ) 785 def test_something(self, unused_obj): 786 pass 787 788 def test_named_test_dict_with_no_name_fails(self): 789 with self.assertRaises(RuntimeError): 790 791 class _(parameterized.TestCase): 792 793 @parameterized.named_parameters( 794 {'unused_obj': 0}, 795 ) 796 def test_something(self, unused_obj): 797 pass 798 799 def test_parameterized_test_iter_has_testcases_property(self): 800 @parameterized.parameters(1, 2, 3, 4, 5, 6) 801 def test_something(unused_self, unused_obj): # pylint: disable=invalid-name 802 pass 803 804 expected_testcases = [1, 2, 3, 4, 5, 6] 805 self.assertTrue(hasattr(test_something, 'testcases')) 806 self.assertCountEqual(expected_testcases, test_something.testcases) 807 808 def test_chained_decorator(self): 809 ts = unittest.makeSuite(self.ChainedTests) 810 self.assertEqual(1, ts.countTestCases()) 811 test = next(t for t in ts) 812 self.assertTrue(hasattr(test, 'test_chained_flavor_strawberry_cone_waffle')) 813 res = unittest.TestResult() 814 815 ts.run(res) 816 self.assertEqual(1, res.testsRun) 817 self.assertTrue(res.wasSuccessful()) 818 819 def test_singleton_list_extraction(self): 820 ts = unittest.makeSuite(self.SingletonListExtraction) 821 res = unittest.TestResult() 822 ts.run(res) 823 self.assertEqual(10, res.testsRun) 824 self.assertTrue(res.wasSuccessful()) 825 826 def test_singleton_argument_extraction(self): 827 ts = unittest.makeSuite(self.SingletonArgumentExtraction) 828 res = unittest.TestResult() 829 ts.run(res) 830 self.assertEqual(9, res.testsRun) 831 self.assertTrue(res.wasSuccessful()) 832 833 def test_singleton_dict_argument(self): 834 ts = unittest.makeSuite(self.SingletonDictArgument) 835 res = unittest.TestResult() 836 ts.run(res) 837 self.assertEqual(1, res.testsRun) 838 self.assertTrue(res.wasSuccessful()) 839 840 def test_decorated_bare_class(self): 841 ts = unittest.makeSuite(self.DecoratedBareClass) 842 res = unittest.TestResult() 843 ts.run(res) 844 self.assertEqual(2, res.testsRun) 845 self.assertTrue(res.wasSuccessful(), msg=str(res.failures)) 846 847 def test_decorated_class(self): 848 ts = unittest.makeSuite(self.DecoratedClass) 849 res = unittest.TestResult() 850 ts.run(res) 851 self.assertEqual(4, res.testsRun) 852 self.assertLen(res.failures, 2) 853 854 def test_generator_decorated_class(self): 855 ts = unittest.makeSuite(self.GeneratorDecoratedClass) 856 res = unittest.TestResult() 857 ts.run(res) 858 self.assertEqual(32, res.testsRun) 859 self.assertLen(res.failures, 16) 860 861 def test_no_duplicate_decorations(self): 862 with self.assertRaises(AssertionError): 863 864 @parameterized.parameters(1, 2, 3, 4) 865 class _(parameterized.TestCase): 866 867 @parameterized.parameters(5, 6, 7, 8) 868 def test_something(self, unused_obj): 869 pass 870 871 def test_double_class_decorations_not_supported(self): 872 @parameterized.parameters('foo', 'bar') 873 class SuperclassWithClassDecorator(parameterized.TestCase): 874 875 def test_name(self, name): 876 del name 877 878 with self.assertRaises(AssertionError): 879 880 @parameterized.parameters('foo', 'bar') 881 class SubclassWithClassDecorator(SuperclassWithClassDecorator): 882 pass 883 884 del SubclassWithClassDecorator 885 886 def test_other_decorator_ordering_unnamed(self): 887 ts = unittest.makeSuite(self.OtherDecoratorUnnamed) 888 res = unittest.TestResult() 889 ts.run(res) 890 # Two for when the parameterized tests call the skip wrapper. 891 # One for when the skip wrapper is called first and doesn't iterate. 892 self.assertEqual(3, res.testsRun) 893 self.assertFalse(res.wasSuccessful()) 894 self.assertEmpty(res.failures) 895 # One error from test_other_then_parameterized. 896 self.assertLen(res.errors, 1) 897 898 def test_other_decorator_ordering_named(self): 899 ts = unittest.makeSuite(self.OtherDecoratorNamed) 900 # Verify it generates the test method names from the original test method. 901 for test in ts: # There is only one test. 902 ts_attributes = dir(test) 903 self.assertIn('test_parameterized_then_other_a', ts_attributes) 904 self.assertIn('test_parameterized_then_other_b', ts_attributes) 905 906 res = unittest.TestResult() 907 ts.run(res) 908 # Two for when the parameterized tests call the skip wrapper. 909 # One for when the skip wrapper is called first and doesn't iterate. 910 self.assertEqual(3, res.testsRun) 911 self.assertFalse(res.wasSuccessful()) 912 self.assertEmpty(res.failures) 913 # One error from test_other_then_parameterized. 914 self.assertLen(res.errors, 1) 915 916 def test_other_decorator_ordering_named_with_dict(self): 917 ts = unittest.makeSuite(self.OtherDecoratorNamedWithDict) 918 # Verify it generates the test method names from the original test method. 919 for test in ts: # There is only one test. 920 ts_attributes = dir(test) 921 self.assertIn('test_parameterized_then_other_a', ts_attributes) 922 self.assertIn('test_parameterized_then_other_b', ts_attributes) 923 924 res = unittest.TestResult() 925 ts.run(res) 926 # Two for when the parameterized tests call the skip wrapper. 927 # One for when the skip wrapper is called first and doesn't iterate. 928 self.assertEqual(3, res.testsRun) 929 self.assertFalse(res.wasSuccessful()) 930 self.assertEmpty(res.failures) 931 # One error from test_other_then_parameterized. 932 self.assertLen(res.errors, 1) 933 934 def test_no_test_error_empty_parameters(self): 935 with self.assertRaises(parameterized.NoTestsError): 936 937 @parameterized.parameters() 938 def test_something(): 939 pass 940 941 del test_something 942 943 def test_no_test_error_empty_generator(self): 944 with self.assertRaises(parameterized.NoTestsError): 945 946 @parameterized.parameters((i for i in [])) 947 def test_something(): 948 pass 949 950 del test_something 951 952 def test_unique_descriptive_names(self): 953 class RecordSuccessTestsResult(unittest.TestResult): 954 955 def __init__(self, *args, **kwargs): 956 super(RecordSuccessTestsResult, self).__init__(*args, **kwargs) 957 self.successful_tests = [] 958 959 def addSuccess(self, test): 960 self.successful_tests.append(test) 961 962 ts = unittest.makeSuite(self.UniqueDescriptiveNamesTest) 963 res = RecordSuccessTestsResult() 964 ts.run(res) 965 self.assertTrue(res.wasSuccessful()) 966 self.assertEqual(2, res.testsRun) 967 test_ids = [test.id() for test in res.successful_tests] 968 full_class_name = unittest.util.strclass(self.UniqueDescriptiveNamesTest) 969 expected_test_ids = [ 970 full_class_name + '.test_normal0 (13)', 971 full_class_name + '.test_normal1 (13)', 972 ] 973 self.assertTrue(test_ids) 974 self.assertCountEqual(expected_test_ids, test_ids) 975 976 def test_multi_generators(self): 977 ts = unittest.makeSuite(self.MultiGeneratorsTestCase) 978 res = unittest.TestResult() 979 ts.run(res) 980 self.assertEqual(2, res.testsRun) 981 self.assertTrue(res.wasSuccessful(), msg=str(res.failures)) 982 983 def test_named_parameters_reusable(self): 984 ts = unittest.makeSuite(self.NamedParametersReusableTestCase) 985 res = unittest.TestResult() 986 ts.run(res) 987 self.assertEqual(8, res.testsRun) 988 self.assertTrue(res.wasSuccessful(), msg=str(res.failures)) 989 990 def test_subclass_inherits_superclass_test_params_reprs(self): 991 self.assertEqual( 992 {'test_name0': "('foo')", 'test_name1': "('bar')"}, 993 self.SuperclassTestCase._test_params_reprs, 994 ) 995 self.assertEqual( 996 {'test_name0': "('foo')", 'test_name1': "('bar')"}, 997 self.SubclassTestCase._test_params_reprs, 998 ) 999 1000 1001# IsolatedAsyncioTestCase is only available in Python 3.8+. 1002if sys.version_info[:2] >= (3, 8): 1003 1004 async def mult(x: float, y: float) -> float: 1005 return x * y 1006 1007 class AsyncTest(unittest.IsolatedAsyncioTestCase, parameterized.TestCase): 1008 1009 def setUp(self): 1010 super().setUp() 1011 self.verify_ran = False 1012 1013 def tearDown(self): 1014 super().tearDown() 1015 1016 # We need the additional check here because originally the test function 1017 # would run, but a coroutine results from the execution and is never 1018 # awaited, so it looked like a successful test run when in fact the 1019 # internal test logic never executed. If you remove the check for 1020 # coroutine and run_until_complete, then set the parameters to fail they 1021 # never will. 1022 self.assertTrue(self.verify_ran) 1023 1024 @parameterized.parameters((1, 2, 2), (2, 2, 4), (3, 2, 6)) 1025 async def test_multiply_expected_matches_actual(self, x, y, expected): 1026 self.assertEqual(await mult(x, y), expected) 1027 self.verify_ran = True 1028 1029 1030def _decorate_with_side_effects(func, self): 1031 self.sideeffect = True 1032 func(self) 1033 1034 1035class CoopMetaclassCreationTest(absltest.TestCase): 1036 1037 class TestBaseMetaclass(type): 1038 1039 def __init__(cls, name, bases, dct): 1040 type.__init__(cls, name, bases, dct) 1041 for member_name, obj in dct.items(): 1042 if member_name.startswith('test'): 1043 setattr( 1044 cls, 1045 member_name, 1046 lambda self, f=obj: _decorate_with_side_effects(f, self), 1047 ) 1048 1049 class TestBase(absltest.TestCase, metaclass=TestBaseMetaclass): 1050 1051 # This test simulates a metaclass that sets some attribute ('sideeffect') 1052 # on each member of the class that starts with 'test'. The test code then 1053 # checks that this attribute exists when the custom metaclass and 1054 # TestGeneratorMetaclass are combined with cooperative inheritance. 1055 1056 # The attribute has to be set in the __init__ method of the metaclass, 1057 # since the TestGeneratorMetaclass already overrides __new__. Only one 1058 # base metaclass can override __new__, but all can provide custom __init__ 1059 # methods. 1060 pass 1061 1062 class MyParams(parameterized.CoopTestCase(TestBase)): 1063 1064 @parameterized.parameters((1, 2, 3), (4, 5, 9)) 1065 def test_addition(self, op1, op2, result): 1066 self.assertEqual(result, op1 + op2) 1067 1068 class MySuite(unittest.TestSuite): 1069 # Under Python 3.4 the TestCases in the suite's list of tests to run are 1070 # destroyed and replaced with None after successful execution by default. 1071 # This disables that behavior. 1072 _cleanup = False 1073 1074 def test_successful_execution(self): 1075 ts = unittest.makeSuite(self.MyParams) 1076 1077 res = unittest.TestResult() 1078 ts.run(res) 1079 self.assertEqual(2, res.testsRun) 1080 self.assertTrue(res.wasSuccessful()) 1081 1082 def test_metaclass_side_effects(self): 1083 ts = unittest.makeSuite(self.MyParams, suiteClass=self.MySuite) 1084 1085 res = unittest.TestResult() 1086 ts.run(res) 1087 self.assertTrue(list(ts)[0].sideeffect) 1088 1089 def test_no_metaclass(self): 1090 class SimpleMixinTestCase(absltest.TestCase): 1091 pass 1092 1093 with self.assertWarnsRegex( 1094 UserWarning, 1095 'CoopTestCase is only necessary when combining with a class that uses a' 1096 ' metaclass', 1097 ) as warning: 1098 parameterized.CoopTestCase(SimpleMixinTestCase) 1099 self.assertEqual( 1100 os.path.basename(warning.filename), 'parameterized_test.py' 1101 ) 1102 1103 1104if __name__ == '__main__': 1105 absltest.main() 1106