Skip to content

Commit bdb9cb1

Browse files
orgadishOr Gadish
and
Or Gadish
authored
Moved @named_data into main ddt.py module so it can be imported. (datadriventests#109)
* Move @named_data into main ddt.py module. Leave NamedData types in a named_data_types.py. * Move named_data_types.py into ddt.py Co-authored-by: Or Gadish <or.gadish@berkeleylights.com>
1 parent 590e2ca commit bdb9cb1

File tree

3 files changed

+92
-100
lines changed

3 files changed

+92
-100
lines changed

ddt.py

+87-2
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,30 @@
3333
INDEX_LEN = '%index_len' # store the index length of the data
3434

3535

36+
# These are helper classes for @named_data that allow ddt tests to have meaningful names.
37+
class NamedDataList(list):
38+
def __init__(self, name, *args):
39+
super(NamedDataList, self).__init__(args)
40+
self.name = name
41+
42+
def __str__(self):
43+
return str(self.name)
44+
45+
46+
class NamedDataDict(dict):
47+
def __init__(self, name, **kwargs):
48+
super(NamedDataDict, self).__init__(kwargs)
49+
self.name = name
50+
51+
def __str__(self):
52+
return str(self.name)
53+
54+
55+
trivial_types = (type(None), bool, int, float, NamedDataList, NamedDataDict)
3656
try:
37-
trivial_types = (type(None), bool, int, float, basestring)
57+
trivial_types += (basestring, )
3858
except NameError:
39-
trivial_types = (type(None), bool, int, float, str)
59+
trivial_types += (str, )
4060

4161

4262
@unique
@@ -382,3 +402,68 @@ def wrapper(cls):
382402
# ``arg`` is the unittest's test class when decorating with ``@ddt`` while
383403
# it is ``None`` when decorating a test class with ``@ddt(k=v)``.
384404
return wrapper(arg) if inspect.isclass(arg) else wrapper
405+
406+
407+
def named_data(*named_values):
408+
"""
409+
This decorator is to allow for meaningful names to be given to tests that would otherwise use @ddt.data and
410+
@ddt.unpack.
411+
412+
Example of original ddt usage:
413+
@ddt.ddt
414+
class TestExample(TemplateTest):
415+
@ddt.data(
416+
[0, 1],
417+
[10, 11]
418+
)
419+
@ddt.unpack
420+
def test_values(self, value1, value2):
421+
...
422+
423+
Example of new usage:
424+
@ddt.ddt
425+
class TestExample(TemplateTest):
426+
@named_data(
427+
['A', 0, 1],
428+
['B', 10, 11],
429+
)
430+
def test_values(self, value1, value2):
431+
...
432+
433+
Note that @unpack is not used.
434+
435+
:param list[Any] | dict[Any,Any] named_values: Each named_value should be a list with the name as the first
436+
argument, or a dictionary with 'name' as one of the keys. The name will be coerced to a string and all other
437+
values will be passed unchanged to the test.
438+
"""
439+
type_of_first = None
440+
values = []
441+
for named_value in named_values:
442+
if type_of_first is None:
443+
type_of_first = type(named_value)
444+
445+
if not isinstance(named_value, type_of_first):
446+
raise TypeError("@named_data expects all values to be of the same type.")
447+
448+
if isinstance(named_value, list):
449+
value = NamedDataList(named_value[0], *named_value[1:])
450+
type_of_first = type_of_first or list
451+
452+
elif isinstance(named_value, dict):
453+
if "name" not in named_value.keys():
454+
raise ValueError("@named_data expects a dictionary with a 'name' key.")
455+
value = NamedDataDict(**named_value)
456+
type_of_first = type_of_first or dict
457+
else:
458+
raise TypeError("@named_data expects a list or dictionary.")
459+
460+
# Remove the __doc__ attribute so @ddt.data doesn't add the NamedData class docstrings to the test name.
461+
value.__doc__ = None
462+
463+
values.append(value)
464+
465+
def wrapper(func):
466+
data(*values)(unpack(func))
467+
return func
468+
469+
return wrapper

named_data.py

-91
This file was deleted.

test/test_named_data.py

+5-7
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,33 @@
11
import ddt
22
import unittest
33

4-
from named_data import named_data
5-
64

75
@ddt.ddt
86
class TestNamedData(unittest.TestCase):
97
class NonTrivialClass(object):
108
pass
119

12-
@named_data(
10+
@ddt.named_data(
1311
['Single', 0, 1]
1412
)
1513
def test_single_named_value(self, value1, value2):
1614
self.assertGreater(value2, value1)
1715

18-
@named_data(
16+
@ddt.named_data(
1917
['1st', 1, 2],
2018
['2nd', 3, 4]
2119
)
2220
def test_multiple_named_value_lists(self, value1, value2):
2321
self.assertGreater(value2, value1)
2422

25-
@named_data(
23+
@ddt.named_data(
2624
dict(name='1st', value2=1, value1=0),
2725
{'name': '2nd', 'value2': 1, 'value1': 0}
2826
)
2927
def test_multiple_named_value_dicts(self, value1, value2):
3028
self.assertGreater(value2, value1)
3129

32-
@named_data(
30+
@ddt.named_data(
3331
['Passes', NonTrivialClass(), True],
3432
['Fails', 1, False]
3533
)
@@ -39,7 +37,7 @@ def test_list_with_nontrivial_type(self, value, passes):
3937
else:
4038
self.assertNotIsInstance(value, self.NonTrivialClass)
4139

42-
@named_data(
40+
@ddt.named_data(
4341
{'name': 'Passes', 'value': NonTrivialClass(), 'passes': True},
4442
{'name': 'Fails', 'value': 1, 'passes': False}
4543
)

0 commit comments

Comments
 (0)