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