Data-Driven/Decorated Tests - A library to multiply test cases
npx @tessl/cli install tessl/pypi-ddt@1.7.00
# DDT (Data-Driven Tests)
1
2
A Python library that allows you to multiply one test case by running it with different test data, making it appear as multiple test cases. DDT provides decorators that transform unittest test methods into data-driven tests, enabling systematic validation of functionality across diverse input conditions.
3
4
## Package Information
5
6
- **Package Name**: ddt
7
- **Package Type**: pypi
8
- **Language**: Python
9
- **Installation**: `pip install ddt`
10
- **Documentation**: http://ddt.readthedocs.org/
11
12
## Core Imports
13
14
```python
15
from ddt import ddt, data, file_data, unpack, idata, named_data, TestNameFormat
16
```
17
18
Or using the module directly:
19
20
```python
21
import ddt
22
```
23
24
## Basic Usage
25
26
```python
27
import unittest
28
from ddt import ddt, data, file_data, unpack, TestNameFormat
29
30
@ddt
31
class MyTestCase(unittest.TestCase):
32
33
@data(1, 2, 3, 4)
34
def test_simple_values(self, value):
35
self.assertTrue(value > 0)
36
37
@data([1, 2], [3, 4], [5, 6])
38
@unpack
39
def test_with_unpack(self, a, b):
40
self.assertLess(a, b)
41
42
@file_data('test_data.json')
43
def test_with_file_data(self, value):
44
self.assertIsNotNone(value)
45
46
@ddt(testNameFormat=TestNameFormat.INDEX_ONLY)
47
class IndexOnlyTestCase(unittest.TestCase):
48
49
@data("hello", "world")
50
def test_with_index_only_names(self, value):
51
self.assertIsInstance(value, str)
52
53
if __name__ == '__main__':
54
unittest.main()
55
```
56
57
## Capabilities
58
59
### Class Decorator
60
61
#### ddt Decorator
62
63
Enables data-driven testing on unittest.TestCase subclasses.
64
65
```python { .api }
66
def ddt(arg=None, **kwargs):
67
"""
68
Class decorator for test cases that enables data-driven testing.
69
70
Parameters:
71
- arg: unittest.TestCase subclass or None (for parameterized decorator usage)
72
- **kwargs: Optional configuration parameters
73
- testNameFormat: TestNameFormat enum value for controlling test name generation
74
75
Returns:
76
Decorated test class with generated test methods
77
"""
78
```
79
80
### Method Decorators
81
82
#### data Decorator
83
84
Provides test data values directly to test methods.
85
86
```python { .api }
87
def data(*values):
88
"""
89
Method decorator to provide test data values.
90
91
Parameters:
92
- *values: Variable number of test data values
93
94
Returns:
95
Decorated test method that runs once per data value
96
"""
97
```
98
99
#### idata Decorator
100
101
Provides test data from an iterable with optional index formatting.
102
103
```python { .api }
104
def idata(iterable, index_len=None):
105
"""
106
Method decorator to provide test data from an iterable.
107
108
Parameters:
109
- iterable: Iterable of test data values
110
- index_len: Optional integer specifying width to zero-pad test indices
111
112
Returns:
113
Decorated test method that runs once per iterable item
114
"""
115
```
116
117
#### file_data Decorator
118
119
Loads test data from JSON or YAML files.
120
121
```python { .api }
122
def file_data(value, yaml_loader=None):
123
"""
124
Method decorator to load test data from files.
125
126
Parameters:
127
- value: Path to JSON/YAML file relative to test file directory
128
- yaml_loader: Optional custom YAML loader function
129
130
Returns:
131
Decorated test method that runs with file-based data
132
"""
133
```
134
135
#### unpack Decorator
136
137
Unpacks tuple/list/dict test data as separate function arguments.
138
139
```python { .api }
140
def unpack(func):
141
"""
142
Method decorator to unpack structured test data.
143
144
Parameters:
145
- func: Test method to be decorated
146
147
Returns:
148
Decorated test method that unpacks data arguments
149
"""
150
```
151
152
#### named_data Decorator
153
154
Provides meaningful names to data-driven tests.
155
156
```python { .api }
157
def named_data(*named_values):
158
"""
159
Method decorator to provide named test data with meaningful test names.
160
161
Parameters:
162
- *named_values: Sequences or dictionaries with 'name' key for test identification
163
164
Returns:
165
Decorated test method with named test cases
166
"""
167
```
168
169
### Utility Functions
170
171
#### mk_test_name Function
172
173
Generates test case names from original name, value, and index.
174
175
```python { .api }
176
def mk_test_name(name, value, index=0, index_len=5, name_fmt=TestNameFormat.DEFAULT):
177
"""
178
Generate a new name for a test case.
179
180
Parameters:
181
- name: Original test method name
182
- value: Test data value
183
- index: Test case index (default: 0)
184
- index_len: Width for zero-padding indices (default: 5)
185
- name_fmt: TestNameFormat enum value for name formatting
186
187
Returns:
188
str: Generated test case name
189
"""
190
```
191
192
#### is_trivial Function
193
194
Determines if a value is a simple/scalar type for name generation.
195
196
```python { .api }
197
def is_trivial(value):
198
"""
199
Check if value is a trivial type for test naming.
200
201
Parameters:
202
- value: Value to check
203
204
Returns:
205
bool: True if value is trivial (scalar or simple container)
206
"""
207
```
208
209
### Configuration
210
211
#### TestNameFormat Enum
212
213
Controls how test names are generated.
214
215
```python { .api }
216
class TestNameFormat(Enum):
217
"""
218
Enum to configure test name composition.
219
220
Values:
221
- DEFAULT (0): Include both index and value in test name
222
- INDEX_ONLY (1): Include only index in test name
223
"""
224
DEFAULT = 0
225
INDEX_ONLY = 1
226
```
227
228
### Helper Classes
229
230
#### _NamedDataList Class
231
232
List subclass with name attribute for test naming (used internally by named_data).
233
234
```python { .api }
235
class _NamedDataList(list):
236
"""
237
List subclass with name attribute for meaningful test names.
238
239
Parameters:
240
- name: Name for the test case
241
- *args: List elements
242
"""
243
def __init__(self, name, *args): ...
244
def __str__(self): ...
245
```
246
247
#### _NamedDataDict Class
248
249
Dict subclass with name attribute for test naming (used internally by named_data).
250
251
```python { .api }
252
class _NamedDataDict(dict):
253
"""
254
Dict subclass with name attribute for meaningful test names.
255
256
Parameters:
257
- **kwargs: Dictionary items including required 'name' key
258
259
Raises:
260
- KeyError: If 'name' key is not provided
261
"""
262
def __init__(self, **kwargs): ...
263
def __str__(self): ...
264
```
265
266
### Constants
267
268
#### __version__
269
270
Package version string.
271
272
```python { .api }
273
__version__ = "1.7.2"
274
```
275
276
## Types
277
278
```python { .api }
279
import unittest
280
from typing import Union, Any, List, Dict, Type, Callable
281
282
# Type aliases for documentation
283
TestData = Union[Any, List[Any], Dict[str, Any]]
284
TestMethod = Callable[..., None]
285
TestClass = Type[unittest.TestCase]
286
```
287
288
## Usage Examples
289
290
### Multiple Data Values
291
292
```python
293
@ddt
294
class TestCalculator(unittest.TestCase):
295
296
@data(2, 4, 6, 8)
297
def test_even_numbers(self, value):
298
self.assertEqual(value % 2, 0)
299
```
300
301
### Complex Data with Unpacking
302
303
```python
304
@ddt
305
class TestMath(unittest.TestCase):
306
307
@data((2, 3, 5), (1, 1, 2), (0, 5, 5))
308
@unpack
309
def test_addition(self, a, b, expected):
310
self.assertEqual(a + b, expected)
311
```
312
313
### File-Based Data
314
315
```python
316
# test_data.json
317
[
318
{"input": "hello", "expected": 5},
319
{"input": "world", "expected": 5}
320
]
321
322
@ddt
323
class TestStringLength(unittest.TestCase):
324
325
@file_data('test_data.json')
326
def test_length(self, value):
327
input_str = value['input']
328
expected = value['expected']
329
self.assertEqual(len(input_str), expected)
330
```
331
332
### Named Data for Better Test Names
333
334
```python
335
@ddt
336
class TestAPI(unittest.TestCase):
337
338
@named_data(
339
['valid_user', 'john@example.com', 200],
340
['invalid_email', 'invalid-email', 400],
341
['empty_email', '', 400]
342
)
343
def test_user_creation(self, email, expected_status):
344
response = create_user(email)
345
self.assertEqual(response.status_code, expected_status)
346
```
347
348
### YAML Data with Custom Loader
349
350
```python
351
import yaml
352
353
# Custom YAML loader for loading specific data types
354
class CustomLoader(yaml.SafeLoader):
355
pass
356
357
def construct_custom_object(loader, node):
358
return {'type': 'custom', 'value': loader.construct_scalar(node)}
359
360
CustomLoader.add_constructor('!custom', construct_custom_object)
361
362
@ddt
363
class TestYAMLData(unittest.TestCase):
364
365
@file_data('test_data.yaml', yaml_loader=CustomLoader)
366
def test_yaml_with_custom_loader(self, value):
367
self.assertIn('type', value)
368
self.assertEqual(value['type'], 'custom')
369
```
370
371
Example `test_data.yaml` file:
372
```yaml
373
- !custom "test_value_1"
374
- !custom "test_value_2"
375
- !custom "test_value_3"
376
```