diff options
author | R David Murray <rdmurray@bitdance.com> | 2012-05-31 01:53:40 (GMT) |
---|---|---|
committer | R David Murray <rdmurray@bitdance.com> | 2012-05-31 01:53:40 (GMT) |
commit | 56517e5cb91c896024934a520d365d6e275eb1ad (patch) | |
tree | 21eb85c9393663238a595cf25d30744150745d02 /Lib/test/test_email/__init__.py | |
parent | 01d7058d6acaf30441bf211580ba9c8d03b3c3c5 (diff) | |
download | cpython-56517e5cb91c896024934a520d365d6e275eb1ad.zip cpython-56517e5cb91c896024934a520d365d6e275eb1ad.tar.gz cpython-56517e5cb91c896024934a520d365d6e275eb1ad.tar.bz2 |
Make parameterized tests in email less hackish.
Or perhaps more hackish, depending on your perspective. But at least this
way it is now possible to run the individual tests using the unittest CLI.
Diffstat (limited to 'Lib/test/test_email/__init__.py')
-rw-r--r-- | Lib/test/test_email/__init__.py | 79 |
1 files changed, 79 insertions, 0 deletions
diff --git a/Lib/test/test_email/__init__.py b/Lib/test/test_email/__init__.py index 75dc64d..bd9d52c 100644 --- a/Lib/test/test_email/__init__.py +++ b/Lib/test/test_email/__init__.py @@ -71,3 +71,82 @@ class TestEmailBase(unittest.TestCase): for i in range(len(actual)): self.assertIsInstance(actual[i], expected[i], 'item {}'.format(i)) + + +# Metaclass to allow for parameterized tests +class Parameterized(type): + + """Provide a test method parameterization facility. + + Parameters are specified as the value of a class attribute that ends with + the string '_params'. Call the portion before '_params' the prefix. Then + a method to be parameterized must have the same prefix, the string + '_as_', and an arbitrary suffix. + + The value of the _params attribute may be either a dictionary or a list. + The values in the dictionary and the elements of the list may either be + single values, or a list. If single values, they are turned into single + element tuples. However derived, the resulting sequence is passed via + *args to the parameterized test function. + + In a _params dictioanry, the keys become part of the name of the generated + tests. In a _params list, the values in the list are converted into a + string by joining the string values of the elements of the tuple by '_' and + converting any blanks into '_'s, and this become part of the name. The + full name of a generated test is the portion of the _params name before the + '_params' portion, plus an '_', plus the name derived as explained above. + + For example, if we have: + + count_params = range(2) + + def count_as_foo_arg(self, foo): + self.assertEqual(foo+1, myfunc(foo)) + + we will get parameterized test methods named: + test_foo_arg_0 + test_foo_arg_1 + test_foo_arg_2 + + Or we could have: + + example_params = {'foo': ('bar', 1), 'bing': ('bang', 2)} + + def example_as_myfunc_input(self, name, count): + self.assertEqual(name+str(count), myfunc(name, count)) + + and get: + test_myfunc_input_foo + test_myfunc_input_bing + + Note: if and only if the generated test name is a valid identifier can it + be used to select the test individually from the unittest command line. + + """ + + def __new__(meta, classname, bases, classdict): + paramdicts = {} + for name, attr in classdict.items(): + if name.endswith('_params'): + if not hasattr(attr, 'keys'): + d = {} + for x in attr: + if not hasattr(x, '__iter__'): + x = (x,) + n = '_'.join(str(v) for v in x).replace(' ', '_') + d[n] = x + attr = d + paramdicts[name[:-7] + '_as_'] = attr + testfuncs = {} + for name, attr in classdict.items(): + for paramsname, paramsdict in paramdicts.items(): + if name.startswith(paramsname): + testnameroot = 'test_' + name[len(paramsname):] + for paramname, params in paramsdict.items(): + test = (lambda self, name=name, params=params: + getattr(self, name)(*params)) + testname = testnameroot + '_' + paramname + test.__name__ = testname + testfuncs[testname] = test + classdict.update(testfuncs) + return super().__new__(meta, classname, bases, classdict) |