33 """Adds support for parameterized tests to Python's unittest TestCase class.
35 A parameterized test is a method in a test case that is invoked with different
40 class AdditionExample(parameterized.TestCase):
41 @parameterized.parameters(
45 def testAddition(self, op1, op2, result):
46 self.assertEqual(result, op1 + op2)
49 Each invocation is a separate test case and properly isolated just
50 like a normal test method, with its own setUp/tearDown cycle. In the
51 example above, there are three separate testcases, one of which will
52 fail due to an assertion error (1 + 1 != 3).
54 Parameters for individual test cases can be tuples (with positional parameters)
55 or dictionaries (with named parameters):
57 class AdditionExample(parameterized.TestCase):
58 @parameterized.parameters(
59 {'op1': 1, 'op2': 2, 'result': 3},
60 {'op1': 4, 'op2': 5, 'result': 9},
62 def testAddition(self, op1, op2, result):
63 self.assertEqual(result, op1 + op2)
65 If a parameterized test fails, the error message will show the
66 original test name (which is modified internally) and the arguments
67 for the specific invocation, which are part of the string returned by
68 the shortDescription() method on test cases.
70 The id method of the test, used internally by the unittest framework,
71 is also modified to show the arguments. To make sure that test names
72 stay the same across several invocations, object representations like
74 >>> class Foo(object):
77 '<__main__.Foo object at 0x23d8610>'
79 are turned into '<__main__.Foo>'. For even more descriptive names,
80 especially in test logs, you can use the named_parameters decorator. In
81 this case, only tuples are supported, and the first parameters has to
82 be a string (or an object that returns an apt name when converted via
85 class NamedExample(parameterized.TestCase):
86 @parameterized.named_parameters(
87 ('Normal', 'aa', 'aaa', True),
88 ('EmptyPrefix', '', 'abc', True),
89 ('BothEmpty', '', '', True))
90 def testStartsWith(self, prefix, string, result):
91 self.assertEqual(result, strings.startswith(prefix))
93 Named tests also have the benefit that they can be run individually
94 from the command line:
96 $ testmodule.py NamedExample.testStartsWithNormal
98 --------------------------------------------------------------------
103 Parameterized Classes
104 =====================
105 If invocation arguments are shared across test methods in a single
106 TestCase class, instead of decorating all test methods
107 individually, the class itself can be decorated:
109 @parameterized.parameters(
112 class ArithmeticTest(parameterized.TestCase):
113 def testAdd(self, arg1, arg2, result):
114 self.assertEqual(arg1 + arg2, result)
116 def testSubtract(self, arg2, arg2, result):
117 self.assertEqual(result - arg1, arg2)
119 Inputs from Iterables
120 =====================
121 If parameters should be shared across several test cases, or are dynamically
122 created from other sources, a single non-tuple iterable can be passed into
123 the decorator. This iterable will be used to obtain the test cases:
125 class AdditionExample(parameterized.TestCase):
126 @parameterized.parameters(
127 c.op1, c.op2, c.result for c in testcases
129 def testAddition(self, op1, op2, result):
130 self.assertEqual(result, op1 + op2)
133 Single-Argument Test Methods
134 ============================
135 If a test method takes only one argument, the single argument does not need to
136 be wrapped into a tuple:
138 class NegativeNumberExample(parameterized.TestCase):
139 @parameterized.parameters(
142 def testIsNegative(self, arg):
143 self.assertTrue(IsNegative(arg))
146 __author__ =
'tmarek@google.com (Torsten Marek)'
152 import unittest2
as unittest
159 import collections.abc
as collections_abc
162 import collections
as collections_abc
164 ADDR_RE = re.compile(
r'<([a-zA-Z0-9_\-\.]+) object at 0x[a-fA-F0-9]+>')
165 _SEPARATOR = uuid.uuid1().hex
166 _FIRST_ARG = object()
167 _ARGUMENT_REPR = object()
171 return ADDR_RE.sub(
r'<\1>', repr(obj))
177 return '%s.%s' % (cls.__module__, cls.__name__)
181 return (isinstance(obj, collections_abc.Iterable)
and
182 not isinstance(obj, str))
186 if isinstance(testcase_params, collections_abc.Mapping):
187 return ', '.join(
'%s=%s' % (argname,
_CleanRepr(value))
188 for argname, value
in testcase_params.items())
190 return ', '.join(
map(_CleanRepr, testcase_params))
195 class _ParameterizedTestIter(object):
196 """Callable and iterable class for producing new test cases."""
198 def __init__(self, test_method, testcases, naming_type):
199 """Returns concrete test functions for a test and a list of parameters.
201 The naming_type is used to determine the name of the concrete
202 functions as reported by the unittest framework. If naming_type is
203 _FIRST_ARG, the testcases must be tuples, and the first element must
204 have a string representation that is a valid Python identifier.
207 test_method: The decorated test method.
208 testcases: (list of tuple/dict) A list of parameter
209 tuples/dicts for individual test invocations.
210 naming_type: The test naming type, either _NAMED or _ARGUMENT_REPR.
217 raise RuntimeError(
'You appear to be running a parameterized test case '
218 'without having inherited from parameterized.'
219 'TestCase. This is bad because none of '
220 'your test cases are actually being run.')
226 def MakeBoundParamTest(testcase_params):
227 @functools.wraps(test_method)
228 def BoundParamTest(self):
229 if isinstance(testcase_params, collections_abc.Mapping):
230 test_method(self, **testcase_params)
232 test_method(self, *testcase_params)
234 test_method(self, testcase_params)
236 if naming_type
is _FIRST_ARG:
239 BoundParamTest.__x_use_name__ =
True
240 BoundParamTest.__name__ +=
str(testcase_params[0])
241 testcase_params = testcase_params[1:]
242 elif naming_type
is _ARGUMENT_REPR:
247 BoundParamTest.__x_extra_id__ =
'(%s)' % (
250 raise RuntimeError(
'%s is not a valid naming type.' % (naming_type,))
252 BoundParamTest.__doc__ =
'%s(%s)' % (
254 if test_method.__doc__:
255 BoundParamTest.__doc__ +=
'\n%s' % (test_method.__doc__,)
256 return BoundParamTest
257 return (MakeBoundParamTest(c)
for c
in self.
testcases)
261 """True iff testcases contains only a single non-tuple element."""
262 return len(testcases) == 1
and not isinstance(testcases[0], tuple)
266 assert not getattr(class_object,
'_id_suffix',
None), (
267 'Cannot add parameters to %s,'
268 ' which already has parameterized methods.' % (class_object,))
269 class_object._id_suffix = id_suffix = {}
272 for name, obj
in class_object.__dict__.copy().
items():
273 if (name.startswith(unittest.TestLoader.testMethodPrefix)
274 and isinstance(obj, types.FunctionType)):
275 delattr(class_object, name)
278 methods, id_suffix, name,
280 for name, meth
in methods.items():
281 setattr(class_object, name, meth)
285 """Implementation of the parameterization decorators.
288 naming_type: The naming type.
289 testcases: Testcase parameters.
292 A function for modifying the decorated object.
295 if isinstance(obj, type):
298 list(testcases)
if not isinstance(testcases, collections_abc.Sequence)
307 'Single parameter argument must be a non-string iterable')
308 testcases = testcases[0]
314 """A decorator for creating parameterized tests.
316 See the module docstring for a usage example.
318 *testcases: Parameters for the decorated method, either a single
319 iterable, or a list of tuples/dicts/objects (for tests
320 with only one argument).
323 A test generator to be handled by TestGeneratorMetaclass.
329 """A decorator for creating parameterized tests.
331 See the module docstring for a usage example. The first element of
332 each parameter tuple should be a string and will be appended to the
333 name of the test method.
336 *testcases: Parameters for the decorated method, either a single
337 iterable, or a list of tuples.
340 A test generator to be handled by TestGeneratorMetaclass.
346 """Metaclass for test cases with test generators.
348 A test generator is an iterable in a testcase that produces callables. These
349 callables must be single-argument methods. These methods are injected into
350 the class namespace and the original iterable is removed. If the name of the
351 iterable conforms to the test pattern, the injected methods will be picked
352 up as tests by the unittest framework.
354 In general, it is supposed to be used in conjunction with the
355 parameters decorator.
359 dct[
'_id_suffix'] = id_suffix = {}
360 for name, obj
in dct.items():
361 if (name.startswith(unittest.TestLoader.testMethodPrefix)
and
367 return type.__new__(mcs, class_name, bases, dct)
371 """Adds individual test cases to a dictionary.
374 dct: The target dictionary.
375 id_suffix: The dictionary for mapping names to test IDs.
376 name: The original name of the test case.
377 iterator: The iterator generating the individual test cases.
379 for idx, func
in enumerate(iterator):
380 assert callable(func),
'Test generators must yield callables, got %r' % (
382 if getattr(func,
'__x_use_name__',
False):
383 new_name = func.__name__
385 new_name =
'%s%s%d' % (name, _SEPARATOR, idx)
386 assert new_name
not in dct, (
387 'Name of parameterized test case "%s" not unique' % (new_name,))
389 id_suffix[new_name] = getattr(func,
'__x_extra_id__',
'')
393 """Base class for test cases using the parameters decorator."""
394 __metaclass__ = TestGeneratorMetaclass
397 return self._testMethodName.
split(_SEPARATOR)[0]
403 """Returns the descriptive ID of the test.
405 This is used internally by the unittesting framework to get a name
406 for the test to be used in reports.
411 return '%s.%s%s' % (
_StrClass(self.__class__),
413 self._id_suffix.
get(self._testMethodName,
''))
417 """Returns a new base class with a cooperative metaclass base.
419 This enables the TestCase to be used in combination
420 with other base classes that have custom metaclasses, such as
423 Only works with metaclasses that do not override type.__new__.
430 from google3.testing.pybase import parameterized
432 class ExampleTest(parameterized.CoopTestCase(mox.MoxTestBase)):
436 other_base_class: (class) A test case base class.
443 (other_base_class.__metaclass__,
444 TestGeneratorMetaclass), {})
447 (other_base_class, TestCase), {})