unittest-parametrize 1.6.0__tar.gz → 1.7.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {unittest_parametrize-1.6.0 → unittest_parametrize-1.7.0}/CHANGELOG.rst +17 -1
- {unittest_parametrize-1.6.0/src/unittest_parametrize.egg-info → unittest_parametrize-1.7.0}/PKG-INFO +126 -37
- {unittest_parametrize-1.6.0 → unittest_parametrize-1.7.0}/README.rst +122 -34
- {unittest_parametrize-1.6.0 → unittest_parametrize-1.7.0}/pyproject.toml +47 -8
- {unittest_parametrize-1.6.0 → unittest_parametrize-1.7.0}/src/unittest_parametrize/__init__.py +40 -13
- {unittest_parametrize-1.6.0 → unittest_parametrize-1.7.0/src/unittest_parametrize.egg-info}/PKG-INFO +126 -37
- {unittest_parametrize-1.6.0 → unittest_parametrize-1.7.0}/LICENSE +0 -0
- {unittest_parametrize-1.6.0 → unittest_parametrize-1.7.0}/MANIFEST.in +0 -0
- {unittest_parametrize-1.6.0 → unittest_parametrize-1.7.0}/setup.cfg +0 -0
- {unittest_parametrize-1.6.0 → unittest_parametrize-1.7.0}/src/unittest_parametrize/py.typed +0 -0
- {unittest_parametrize-1.6.0 → unittest_parametrize-1.7.0}/src/unittest_parametrize.egg-info/SOURCES.txt +0 -0
- {unittest_parametrize-1.6.0 → unittest_parametrize-1.7.0}/src/unittest_parametrize.egg-info/dependency_links.txt +0 -0
- {unittest_parametrize-1.6.0 → unittest_parametrize-1.7.0}/src/unittest_parametrize.egg-info/requires.txt +0 -0
- {unittest_parametrize-1.6.0 → unittest_parametrize-1.7.0}/src/unittest_parametrize.egg-info/top_level.txt +0 -0
@@ -2,10 +2,26 @@
|
|
2
2
|
Changelog
|
3
3
|
=========
|
4
4
|
|
5
|
+
1.7.0 (2025-08-27)
|
6
|
+
------------------
|
7
|
+
|
8
|
+
* Add support for the ``ids`` argument being a callable.
|
9
|
+
The callable is called once per parameter value and can return a string for that value or ``None`` to use the default.
|
10
|
+
|
11
|
+
`PR #143 <https://github.com/adamchainz/unittest-parametrize/pull/143>`__.
|
12
|
+
|
13
|
+
* Fail for collisions between autogenerated IDs and user-specified IDs when mixing tuples and ``param`` instances.
|
14
|
+
|
15
|
+
`PR #143 <https://github.com/adamchainz/unittest-parametrize/pull/143>`__.
|
16
|
+
|
17
|
+
* Update the type hints to allow mixing tuples and ``param`` instances.
|
18
|
+
|
19
|
+
`PR #143 <https://github.com/adamchainz/unittest-parametrize/pull/143>`__.
|
20
|
+
|
5
21
|
1.6.0 (2025-01-06)
|
6
22
|
------------------
|
7
23
|
|
8
|
-
* Add
|
24
|
+
* Add support for asynchronous tests.
|
9
25
|
|
10
26
|
Thanks to Adrien Cossa in `PR #121 <https://github.com/adamchainz/unittest-parametrize/pull/121>`__.
|
11
27
|
|
{unittest_parametrize-1.6.0/src/unittest_parametrize.egg-info → unittest_parametrize-1.7.0}/PKG-INFO
RENAMED
@@ -1,15 +1,15 @@
|
|
1
|
-
Metadata-Version: 2.
|
1
|
+
Metadata-Version: 2.4
|
2
2
|
Name: unittest-parametrize
|
3
|
-
Version: 1.
|
3
|
+
Version: 1.7.0
|
4
4
|
Summary: Parametrize tests within unittest TestCases.
|
5
5
|
Author-email: Adam Johnson <me@adamj.eu>
|
6
|
+
License-Expression: MIT
|
6
7
|
Project-URL: Changelog, https://github.com/adamchainz/unittest-parametrize/blob/main/CHANGELOG.rst
|
7
8
|
Project-URL: Funding, https://adamj.eu/books/
|
8
9
|
Project-URL: Repository, https://github.com/adamchainz/unittest-parametrize
|
9
10
|
Keywords: unittest
|
10
11
|
Classifier: Development Status :: 5 - Production/Stable
|
11
12
|
Classifier: Intended Audience :: Developers
|
12
|
-
Classifier: License :: OSI Approved :: MIT License
|
13
13
|
Classifier: Natural Language :: English
|
14
14
|
Classifier: Programming Language :: Python :: 3 :: Only
|
15
15
|
Classifier: Programming Language :: Python :: 3.9
|
@@ -22,6 +22,7 @@ Requires-Python: >=3.9
|
|
22
22
|
Description-Content-Type: text/x-rst
|
23
23
|
License-File: LICENSE
|
24
24
|
Requires-Dist: typing-extensions; python_version < "3.10"
|
25
|
+
Dynamic: license-file
|
25
26
|
|
26
27
|
====================
|
27
28
|
unittest-parametrize
|
@@ -83,8 +84,7 @@ Here’s a basic example:
|
|
83
84
|
|
84
85
|
.. code-block:: python
|
85
86
|
|
86
|
-
from unittest_parametrize import parametrize
|
87
|
-
from unittest_parametrize import ParametrizedTestCase
|
87
|
+
from unittest_parametrize import ParametrizedTestCase, parametrize
|
88
88
|
|
89
89
|
|
90
90
|
class SquareTests(ParametrizedTestCase):
|
@@ -113,8 +113,7 @@ You can provide argument names as a sequence of strings instead:
|
|
113
113
|
|
114
114
|
.. code-block:: python
|
115
115
|
|
116
|
-
from unittest_parametrize import parametrize
|
117
|
-
from unittest_parametrize import ParametrizedTestCase
|
116
|
+
from unittest_parametrize import ParametrizedTestCase, parametrize
|
118
117
|
|
119
118
|
|
120
119
|
class SquareTests(ParametrizedTestCase):
|
@@ -177,13 +176,20 @@ You can see these names when running the tests:
|
|
177
176
|
|
178
177
|
OK
|
179
178
|
|
180
|
-
You can customize these names
|
179
|
+
You can customize these names in several ways:
|
180
|
+
|
181
|
+
1. Using ``param`` objects with IDs.
|
182
|
+
2. Passing a sequence of strings as the ``ids`` argument.
|
183
|
+
3. Passing a callable as the ``ids`` argument.
|
184
|
+
|
185
|
+
Passing ``param`` objects with IDs
|
186
|
+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
187
|
+
|
188
|
+
Pass a ``param`` object for each parameter set, setting the test ID suffix with the optional ``id`` argument:
|
181
189
|
|
182
190
|
.. code-block:: python
|
183
191
|
|
184
|
-
from unittest_parametrize import param
|
185
|
-
from unittest_parametrize import parametrize
|
186
|
-
from unittest_parametrize import ParametrizedTestCase
|
192
|
+
from unittest_parametrize import ParametrizedTestCase, param, parametrize
|
187
193
|
|
188
194
|
|
189
195
|
class SquareTests(ParametrizedTestCase):
|
@@ -197,7 +203,7 @@ You can customize these names by passing ``param`` objects, which contain the ar
|
|
197
203
|
def test_square(self, x: int, expected: int) -> None:
|
198
204
|
self.assertEqual(x**2, expected)
|
199
205
|
|
200
|
-
Yielding
|
206
|
+
Yielding more natural names:
|
201
207
|
|
202
208
|
.. code-block:: console
|
203
209
|
|
@@ -216,9 +222,7 @@ Since parameter IDs are optional, you can provide them only for some tests:
|
|
216
222
|
|
217
223
|
.. code-block:: python
|
218
224
|
|
219
|
-
from unittest_parametrize import param
|
220
|
-
from unittest_parametrize import parametrize
|
221
|
-
from unittest_parametrize import ParametrizedTestCase
|
225
|
+
from unittest_parametrize import ParametrizedTestCase, param, parametrize
|
222
226
|
|
223
227
|
|
224
228
|
class SquareTests(ParametrizedTestCase):
|
@@ -232,7 +236,7 @@ Since parameter IDs are optional, you can provide them only for some tests:
|
|
232
236
|
def test_square(self, x: int, expected: int) -> None:
|
233
237
|
self.assertEqual(x**2, expected)
|
234
238
|
|
235
|
-
ID-free ``param``\s fall back to the default index suffixes:
|
239
|
+
The ID-free ``param``\s fall back to the default index suffixes:
|
236
240
|
|
237
241
|
.. code-block:: console
|
238
242
|
|
@@ -245,12 +249,14 @@ ID-free ``param``\s fall back to the default index suffixes:
|
|
245
249
|
|
246
250
|
OK
|
247
251
|
|
248
|
-
|
252
|
+
Passing a sequence of strings as the ``ids`` argument
|
253
|
+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
254
|
+
|
255
|
+
Another option is to provide the IDs in the separate ``ids`` argument:
|
249
256
|
|
250
257
|
.. code-block:: python
|
251
258
|
|
252
|
-
from unittest_parametrize import parametrize
|
253
|
-
from unittest_parametrize import ParametrizedTestCase
|
259
|
+
from unittest_parametrize import ParametrizedTestCase, parametrize
|
254
260
|
|
255
261
|
|
256
262
|
class SquareTests(ParametrizedTestCase):
|
@@ -265,6 +271,64 @@ Alternatively, you can provide the id’s separately with the ``ids`` argument:
|
|
265
271
|
def test_square(self, x: int, expected: int) -> None:
|
266
272
|
self.assertEqual(x**2, expected)
|
267
273
|
|
274
|
+
This option sets the full suffixes to the provided strings:
|
275
|
+
|
276
|
+
.. code-block:: console
|
277
|
+
|
278
|
+
$ python -m unittest t.py -v
|
279
|
+
test_square_one (example.SquareTests.test_square_one) ... ok
|
280
|
+
test_square_two (example.SquareTests.test_square_two) ... ok
|
281
|
+
|
282
|
+
----------------------------------------------------------------------
|
283
|
+
Ran 2 tests in 0.000s
|
284
|
+
|
285
|
+
OK
|
286
|
+
|
287
|
+
Passing a callable as the ``ids`` argument
|
288
|
+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
289
|
+
|
290
|
+
The ``ids`` argument can also be a callable, which unittest-parametrize calls once per parameter value.
|
291
|
+
The callable can return a string for that value, or ``None`` to use the default index suffix.
|
292
|
+
The values are then joined with underscores to form the full suffix.
|
293
|
+
|
294
|
+
For example:
|
295
|
+
|
296
|
+
.. code-block:: python
|
297
|
+
|
298
|
+
from unittest_parametrize import ParametrizedTestCase, parametrize
|
299
|
+
|
300
|
+
|
301
|
+
def make_id(value):
|
302
|
+
if isinstance(value, int):
|
303
|
+
return f"num{value}"
|
304
|
+
return None
|
305
|
+
|
306
|
+
|
307
|
+
class SquareTests(ParametrizedTestCase):
|
308
|
+
@parametrize(
|
309
|
+
"x,expected",
|
310
|
+
[
|
311
|
+
(1, 1),
|
312
|
+
(2, 4),
|
313
|
+
],
|
314
|
+
ids=make_id,
|
315
|
+
)
|
316
|
+
def test_square(self, x: int, expected: int) -> None:
|
317
|
+
self.assertEqual(x**2, expected)
|
318
|
+
|
319
|
+
…yields:
|
320
|
+
|
321
|
+
.. code-block:: console
|
322
|
+
|
323
|
+
$ python -m unittest t.py -v
|
324
|
+
test_square_num1_num1 (example.SquareTests.test_square_num1_num1) ... ok
|
325
|
+
test_square_num2_num4 (example.SquareTests.test_square_num2_num4) ... ok
|
326
|
+
|
327
|
+
----------------------------------------------------------------------
|
328
|
+
Ran 2 tests in 0.000s
|
329
|
+
|
330
|
+
OK
|
331
|
+
|
268
332
|
Use with other test decorators
|
269
333
|
------------------------------
|
270
334
|
|
@@ -275,8 +339,7 @@ So decorators like ``@mock.patch`` need be beneath ``@parametrize``:
|
|
275
339
|
.. code-block:: python
|
276
340
|
|
277
341
|
from unittest import mock
|
278
|
-
from unittest_parametrize import parametrize
|
279
|
-
from unittest_parametrize import ParametrizedTestCase
|
342
|
+
from unittest_parametrize import ParametrizedTestCase, parametrize
|
280
343
|
|
281
344
|
|
282
345
|
class CarpentryTests(ParametrizedTestCase):
|
@@ -285,8 +348,7 @@ So decorators like ``@mock.patch`` need be beneath ``@parametrize``:
|
|
285
348
|
[(11,), (17,)],
|
286
349
|
)
|
287
350
|
@mock.patch("example.hammer", autospec=True)
|
288
|
-
def test_nail_a_board(self, mock_hammer, nails):
|
289
|
-
...
|
351
|
+
def test_nail_a_board(self, mock_hammer, nails): ...
|
290
352
|
|
291
353
|
Also note that due to how ``mock.patch`` always adds positional arguments at the start, the parametrized arguments must come last.
|
292
354
|
``@parametrize`` always adds parameters as keyword arguments, so you can also use `keyword-only syntax <https://peps.python.org/pep-3102/>`__ for parametrized arguments:
|
@@ -294,8 +356,7 @@ Also note that due to how ``mock.patch`` always adds positional arguments at the
|
|
294
356
|
.. code-block:: python
|
295
357
|
|
296
358
|
# ...
|
297
|
-
def test_nail_a_board(self, mock_hammer, *, nails):
|
298
|
-
...
|
359
|
+
def test_nail_a_board(self, mock_hammer, *, nails): ...
|
299
360
|
|
300
361
|
Multiple ``@parametrize`` decorators
|
301
362
|
------------------------------------
|
@@ -305,8 +366,7 @@ To create a cross-product of tests, you can use nested list comprehensions:
|
|
305
366
|
|
306
367
|
.. code-block:: python
|
307
368
|
|
308
|
-
from unittest_parametrize import parametrize
|
309
|
-
from unittest_parametrize import ParametrizedTestCase
|
369
|
+
from unittest_parametrize import ParametrizedTestCase, parametrize
|
310
370
|
|
311
371
|
|
312
372
|
class RocketTests(ParametrizedTestCase):
|
@@ -318,8 +378,7 @@ To create a cross-product of tests, you can use nested list comprehensions:
|
|
318
378
|
for hyperdrive_level in [0, 1, 2]
|
319
379
|
],
|
320
380
|
)
|
321
|
-
def test_takeoff(self, use_ions, hyperdrive_level) -> None:
|
322
|
-
...
|
381
|
+
def test_takeoff(self, use_ions, hyperdrive_level) -> None: ...
|
323
382
|
|
324
383
|
The above creates 2 * 3 = 6 versions of ``test_takeoff``.
|
325
384
|
|
@@ -331,8 +390,7 @@ __ https://docs.python.org/3/library/itertools.html#itertools.product
|
|
331
390
|
.. code-block:: python
|
332
391
|
|
333
392
|
from itertools import product
|
334
|
-
from unittest_parametrize import parametrize
|
335
|
-
from unittest_parametrize import ParametrizedTestCase
|
393
|
+
from unittest_parametrize import ParametrizedTestCase, parametrize
|
336
394
|
|
337
395
|
|
338
396
|
class RocketTests(ParametrizedTestCase):
|
@@ -346,8 +404,7 @@ __ https://docs.python.org/3/library/itertools.html#itertools.product
|
|
346
404
|
)
|
347
405
|
),
|
348
406
|
)
|
349
|
-
def test_takeoff(self, use_ions, hyperdrive_level, nose_colour) -> None:
|
350
|
-
...
|
407
|
+
def test_takeoff(self, use_ions, hyperdrive_level, nose_colour) -> None: ...
|
351
408
|
|
352
409
|
The above creates 2 * 3 * 2 = 12 versions of ``test_takeoff``.
|
353
410
|
|
@@ -371,15 +428,47 @@ To parametrize all tests within a test case, create a separate decorator and app
|
|
371
428
|
|
372
429
|
class StatsTests(ParametrizedTestCase):
|
373
430
|
@parametrize_race
|
374
|
-
def test_strength(self, race: str) -> None:
|
375
|
-
...
|
431
|
+
def test_strength(self, race: str) -> None: ...
|
376
432
|
|
377
433
|
@parametrize_race
|
378
|
-
def test_dexterity(self, race: str) -> None:
|
379
|
-
...
|
434
|
+
def test_dexterity(self, race: str) -> None: ...
|
380
435
|
|
381
436
|
...
|
382
437
|
|
438
|
+
Pass parameters in a dataclass
|
439
|
+
------------------------------
|
440
|
+
|
441
|
+
Thanks to `Florian Bruhin <https://bruhin.software/>`__ for this tip, from his `pytest tips and tricks presentation <https://bruhin.software/>`__.
|
442
|
+
|
443
|
+
If your test uses many parameters or cases, the parametrization may become unwieldy, as cases don’t name the arguments.
|
444
|
+
In this case, try using a `dataclass <https://docs.python.org/3/library/dataclasses.html>`__ to hold the arguments:
|
445
|
+
|
446
|
+
.. code-block:: python
|
447
|
+
|
448
|
+
from dataclasses import dataclass
|
449
|
+
|
450
|
+
from unittest_parametrize import ParametrizedTestCase, parametrize
|
451
|
+
|
452
|
+
|
453
|
+
@dataclass
|
454
|
+
class SquareParams:
|
455
|
+
x: int
|
456
|
+
expected: int
|
457
|
+
|
458
|
+
|
459
|
+
class SquareTests(ParametrizedTestCase):
|
460
|
+
@parametrize(
|
461
|
+
"sp",
|
462
|
+
[
|
463
|
+
(SquareParams(x=1, expected=1),),
|
464
|
+
(SquareParams(x=2, expected=4),),
|
465
|
+
],
|
466
|
+
)
|
467
|
+
def test_square(self, sp: SquareParams) -> None:
|
468
|
+
self.assertEqual(sp.x**2, sp.expected)
|
469
|
+
|
470
|
+
This way, each parameter is type-checked and named, improving safety and readability.
|
471
|
+
|
383
472
|
History
|
384
473
|
=======
|
385
474
|
|
@@ -58,8 +58,7 @@ Here’s a basic example:
|
|
58
58
|
|
59
59
|
.. code-block:: python
|
60
60
|
|
61
|
-
from unittest_parametrize import parametrize
|
62
|
-
from unittest_parametrize import ParametrizedTestCase
|
61
|
+
from unittest_parametrize import ParametrizedTestCase, parametrize
|
63
62
|
|
64
63
|
|
65
64
|
class SquareTests(ParametrizedTestCase):
|
@@ -88,8 +87,7 @@ You can provide argument names as a sequence of strings instead:
|
|
88
87
|
|
89
88
|
.. code-block:: python
|
90
89
|
|
91
|
-
from unittest_parametrize import parametrize
|
92
|
-
from unittest_parametrize import ParametrizedTestCase
|
90
|
+
from unittest_parametrize import ParametrizedTestCase, parametrize
|
93
91
|
|
94
92
|
|
95
93
|
class SquareTests(ParametrizedTestCase):
|
@@ -152,13 +150,20 @@ You can see these names when running the tests:
|
|
152
150
|
|
153
151
|
OK
|
154
152
|
|
155
|
-
You can customize these names
|
153
|
+
You can customize these names in several ways:
|
154
|
+
|
155
|
+
1. Using ``param`` objects with IDs.
|
156
|
+
2. Passing a sequence of strings as the ``ids`` argument.
|
157
|
+
3. Passing a callable as the ``ids`` argument.
|
158
|
+
|
159
|
+
Passing ``param`` objects with IDs
|
160
|
+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
161
|
+
|
162
|
+
Pass a ``param`` object for each parameter set, setting the test ID suffix with the optional ``id`` argument:
|
156
163
|
|
157
164
|
.. code-block:: python
|
158
165
|
|
159
|
-
from unittest_parametrize import param
|
160
|
-
from unittest_parametrize import parametrize
|
161
|
-
from unittest_parametrize import ParametrizedTestCase
|
166
|
+
from unittest_parametrize import ParametrizedTestCase, param, parametrize
|
162
167
|
|
163
168
|
|
164
169
|
class SquareTests(ParametrizedTestCase):
|
@@ -172,7 +177,7 @@ You can customize these names by passing ``param`` objects, which contain the ar
|
|
172
177
|
def test_square(self, x: int, expected: int) -> None:
|
173
178
|
self.assertEqual(x**2, expected)
|
174
179
|
|
175
|
-
Yielding
|
180
|
+
Yielding more natural names:
|
176
181
|
|
177
182
|
.. code-block:: console
|
178
183
|
|
@@ -191,9 +196,7 @@ Since parameter IDs are optional, you can provide them only for some tests:
|
|
191
196
|
|
192
197
|
.. code-block:: python
|
193
198
|
|
194
|
-
from unittest_parametrize import param
|
195
|
-
from unittest_parametrize import parametrize
|
196
|
-
from unittest_parametrize import ParametrizedTestCase
|
199
|
+
from unittest_parametrize import ParametrizedTestCase, param, parametrize
|
197
200
|
|
198
201
|
|
199
202
|
class SquareTests(ParametrizedTestCase):
|
@@ -207,7 +210,7 @@ Since parameter IDs are optional, you can provide them only for some tests:
|
|
207
210
|
def test_square(self, x: int, expected: int) -> None:
|
208
211
|
self.assertEqual(x**2, expected)
|
209
212
|
|
210
|
-
ID-free ``param``\s fall back to the default index suffixes:
|
213
|
+
The ID-free ``param``\s fall back to the default index suffixes:
|
211
214
|
|
212
215
|
.. code-block:: console
|
213
216
|
|
@@ -220,12 +223,14 @@ ID-free ``param``\s fall back to the default index suffixes:
|
|
220
223
|
|
221
224
|
OK
|
222
225
|
|
223
|
-
|
226
|
+
Passing a sequence of strings as the ``ids`` argument
|
227
|
+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
228
|
+
|
229
|
+
Another option is to provide the IDs in the separate ``ids`` argument:
|
224
230
|
|
225
231
|
.. code-block:: python
|
226
232
|
|
227
|
-
from unittest_parametrize import parametrize
|
228
|
-
from unittest_parametrize import ParametrizedTestCase
|
233
|
+
from unittest_parametrize import ParametrizedTestCase, parametrize
|
229
234
|
|
230
235
|
|
231
236
|
class SquareTests(ParametrizedTestCase):
|
@@ -240,6 +245,64 @@ Alternatively, you can provide the id’s separately with the ``ids`` argument:
|
|
240
245
|
def test_square(self, x: int, expected: int) -> None:
|
241
246
|
self.assertEqual(x**2, expected)
|
242
247
|
|
248
|
+
This option sets the full suffixes to the provided strings:
|
249
|
+
|
250
|
+
.. code-block:: console
|
251
|
+
|
252
|
+
$ python -m unittest t.py -v
|
253
|
+
test_square_one (example.SquareTests.test_square_one) ... ok
|
254
|
+
test_square_two (example.SquareTests.test_square_two) ... ok
|
255
|
+
|
256
|
+
----------------------------------------------------------------------
|
257
|
+
Ran 2 tests in 0.000s
|
258
|
+
|
259
|
+
OK
|
260
|
+
|
261
|
+
Passing a callable as the ``ids`` argument
|
262
|
+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
263
|
+
|
264
|
+
The ``ids`` argument can also be a callable, which unittest-parametrize calls once per parameter value.
|
265
|
+
The callable can return a string for that value, or ``None`` to use the default index suffix.
|
266
|
+
The values are then joined with underscores to form the full suffix.
|
267
|
+
|
268
|
+
For example:
|
269
|
+
|
270
|
+
.. code-block:: python
|
271
|
+
|
272
|
+
from unittest_parametrize import ParametrizedTestCase, parametrize
|
273
|
+
|
274
|
+
|
275
|
+
def make_id(value):
|
276
|
+
if isinstance(value, int):
|
277
|
+
return f"num{value}"
|
278
|
+
return None
|
279
|
+
|
280
|
+
|
281
|
+
class SquareTests(ParametrizedTestCase):
|
282
|
+
@parametrize(
|
283
|
+
"x,expected",
|
284
|
+
[
|
285
|
+
(1, 1),
|
286
|
+
(2, 4),
|
287
|
+
],
|
288
|
+
ids=make_id,
|
289
|
+
)
|
290
|
+
def test_square(self, x: int, expected: int) -> None:
|
291
|
+
self.assertEqual(x**2, expected)
|
292
|
+
|
293
|
+
…yields:
|
294
|
+
|
295
|
+
.. code-block:: console
|
296
|
+
|
297
|
+
$ python -m unittest t.py -v
|
298
|
+
test_square_num1_num1 (example.SquareTests.test_square_num1_num1) ... ok
|
299
|
+
test_square_num2_num4 (example.SquareTests.test_square_num2_num4) ... ok
|
300
|
+
|
301
|
+
----------------------------------------------------------------------
|
302
|
+
Ran 2 tests in 0.000s
|
303
|
+
|
304
|
+
OK
|
305
|
+
|
243
306
|
Use with other test decorators
|
244
307
|
------------------------------
|
245
308
|
|
@@ -250,8 +313,7 @@ So decorators like ``@mock.patch`` need be beneath ``@parametrize``:
|
|
250
313
|
.. code-block:: python
|
251
314
|
|
252
315
|
from unittest import mock
|
253
|
-
from unittest_parametrize import parametrize
|
254
|
-
from unittest_parametrize import ParametrizedTestCase
|
316
|
+
from unittest_parametrize import ParametrizedTestCase, parametrize
|
255
317
|
|
256
318
|
|
257
319
|
class CarpentryTests(ParametrizedTestCase):
|
@@ -260,8 +322,7 @@ So decorators like ``@mock.patch`` need be beneath ``@parametrize``:
|
|
260
322
|
[(11,), (17,)],
|
261
323
|
)
|
262
324
|
@mock.patch("example.hammer", autospec=True)
|
263
|
-
def test_nail_a_board(self, mock_hammer, nails):
|
264
|
-
...
|
325
|
+
def test_nail_a_board(self, mock_hammer, nails): ...
|
265
326
|
|
266
327
|
Also note that due to how ``mock.patch`` always adds positional arguments at the start, the parametrized arguments must come last.
|
267
328
|
``@parametrize`` always adds parameters as keyword arguments, so you can also use `keyword-only syntax <https://peps.python.org/pep-3102/>`__ for parametrized arguments:
|
@@ -269,8 +330,7 @@ Also note that due to how ``mock.patch`` always adds positional arguments at the
|
|
269
330
|
.. code-block:: python
|
270
331
|
|
271
332
|
# ...
|
272
|
-
def test_nail_a_board(self, mock_hammer, *, nails):
|
273
|
-
...
|
333
|
+
def test_nail_a_board(self, mock_hammer, *, nails): ...
|
274
334
|
|
275
335
|
Multiple ``@parametrize`` decorators
|
276
336
|
------------------------------------
|
@@ -280,8 +340,7 @@ To create a cross-product of tests, you can use nested list comprehensions:
|
|
280
340
|
|
281
341
|
.. code-block:: python
|
282
342
|
|
283
|
-
from unittest_parametrize import parametrize
|
284
|
-
from unittest_parametrize import ParametrizedTestCase
|
343
|
+
from unittest_parametrize import ParametrizedTestCase, parametrize
|
285
344
|
|
286
345
|
|
287
346
|
class RocketTests(ParametrizedTestCase):
|
@@ -293,8 +352,7 @@ To create a cross-product of tests, you can use nested list comprehensions:
|
|
293
352
|
for hyperdrive_level in [0, 1, 2]
|
294
353
|
],
|
295
354
|
)
|
296
|
-
def test_takeoff(self, use_ions, hyperdrive_level) -> None:
|
297
|
-
...
|
355
|
+
def test_takeoff(self, use_ions, hyperdrive_level) -> None: ...
|
298
356
|
|
299
357
|
The above creates 2 * 3 = 6 versions of ``test_takeoff``.
|
300
358
|
|
@@ -306,8 +364,7 @@ __ https://docs.python.org/3/library/itertools.html#itertools.product
|
|
306
364
|
.. code-block:: python
|
307
365
|
|
308
366
|
from itertools import product
|
309
|
-
from unittest_parametrize import parametrize
|
310
|
-
from unittest_parametrize import ParametrizedTestCase
|
367
|
+
from unittest_parametrize import ParametrizedTestCase, parametrize
|
311
368
|
|
312
369
|
|
313
370
|
class RocketTests(ParametrizedTestCase):
|
@@ -321,8 +378,7 @@ __ https://docs.python.org/3/library/itertools.html#itertools.product
|
|
321
378
|
)
|
322
379
|
),
|
323
380
|
)
|
324
|
-
def test_takeoff(self, use_ions, hyperdrive_level, nose_colour) -> None:
|
325
|
-
...
|
381
|
+
def test_takeoff(self, use_ions, hyperdrive_level, nose_colour) -> None: ...
|
326
382
|
|
327
383
|
The above creates 2 * 3 * 2 = 12 versions of ``test_takeoff``.
|
328
384
|
|
@@ -346,15 +402,47 @@ To parametrize all tests within a test case, create a separate decorator and app
|
|
346
402
|
|
347
403
|
class StatsTests(ParametrizedTestCase):
|
348
404
|
@parametrize_race
|
349
|
-
def test_strength(self, race: str) -> None:
|
350
|
-
...
|
405
|
+
def test_strength(self, race: str) -> None: ...
|
351
406
|
|
352
407
|
@parametrize_race
|
353
|
-
def test_dexterity(self, race: str) -> None:
|
354
|
-
...
|
408
|
+
def test_dexterity(self, race: str) -> None: ...
|
355
409
|
|
356
410
|
...
|
357
411
|
|
412
|
+
Pass parameters in a dataclass
|
413
|
+
------------------------------
|
414
|
+
|
415
|
+
Thanks to `Florian Bruhin <https://bruhin.software/>`__ for this tip, from his `pytest tips and tricks presentation <https://bruhin.software/>`__.
|
416
|
+
|
417
|
+
If your test uses many parameters or cases, the parametrization may become unwieldy, as cases don’t name the arguments.
|
418
|
+
In this case, try using a `dataclass <https://docs.python.org/3/library/dataclasses.html>`__ to hold the arguments:
|
419
|
+
|
420
|
+
.. code-block:: python
|
421
|
+
|
422
|
+
from dataclasses import dataclass
|
423
|
+
|
424
|
+
from unittest_parametrize import ParametrizedTestCase, parametrize
|
425
|
+
|
426
|
+
|
427
|
+
@dataclass
|
428
|
+
class SquareParams:
|
429
|
+
x: int
|
430
|
+
expected: int
|
431
|
+
|
432
|
+
|
433
|
+
class SquareTests(ParametrizedTestCase):
|
434
|
+
@parametrize(
|
435
|
+
"sp",
|
436
|
+
[
|
437
|
+
(SquareParams(x=1, expected=1),),
|
438
|
+
(SquareParams(x=2, expected=4),),
|
439
|
+
],
|
440
|
+
)
|
441
|
+
def test_square(self, sp: SquareParams) -> None:
|
442
|
+
self.assertEqual(sp.x**2, sp.expected)
|
443
|
+
|
444
|
+
This way, each parameter is type-checked and named, improving safety and readability.
|
445
|
+
|
358
446
|
History
|
359
447
|
=======
|
360
448
|
|
@@ -1,17 +1,19 @@
|
|
1
1
|
[build-system]
|
2
2
|
build-backend = "setuptools.build_meta"
|
3
3
|
requires = [
|
4
|
-
"setuptools",
|
4
|
+
"setuptools>=77",
|
5
5
|
]
|
6
6
|
|
7
7
|
[project]
|
8
8
|
name = "unittest-parametrize"
|
9
|
-
version = "1.
|
9
|
+
version = "1.7.0"
|
10
10
|
description = "Parametrize tests within unittest TestCases."
|
11
11
|
readme = "README.rst"
|
12
12
|
keywords = [
|
13
13
|
"unittest",
|
14
14
|
]
|
15
|
+
license = "MIT"
|
16
|
+
license-files = [ "LICENSE" ]
|
15
17
|
authors = [
|
16
18
|
{ name = "Adam Johnson", email = "me@adamj.eu" },
|
17
19
|
]
|
@@ -19,7 +21,6 @@ requires-python = ">=3.9"
|
|
19
21
|
classifiers = [
|
20
22
|
"Development Status :: 5 - Production/Stable",
|
21
23
|
"Intended Audience :: Developers",
|
22
|
-
"License :: OSI Approved :: MIT License",
|
23
24
|
"Natural Language :: English",
|
24
25
|
"Programming Language :: Python :: 3 :: Only",
|
25
26
|
"Programming Language :: Python :: 3.9",
|
@@ -36,12 +37,50 @@ urls.Changelog = "https://github.com/adamchainz/unittest-parametrize/blob/main/C
|
|
36
37
|
urls.Funding = "https://adamj.eu/books/"
|
37
38
|
urls.Repository = "https://github.com/adamchainz/unittest-parametrize"
|
38
39
|
|
39
|
-
[
|
40
|
-
|
41
|
-
"
|
40
|
+
[dependency-groups]
|
41
|
+
test = [
|
42
|
+
"coverage[toml]",
|
43
|
+
"pytest",
|
44
|
+
"pytest-randomly",
|
45
|
+
"typing-extensions; python_version<'3.10'",
|
46
|
+
]
|
47
|
+
|
48
|
+
[tool.ruff]
|
49
|
+
lint.select = [
|
50
|
+
# flake8-bugbear
|
51
|
+
"B",
|
52
|
+
# flake8-comprehensions
|
53
|
+
"C4",
|
54
|
+
# pycodestyle
|
55
|
+
"E",
|
56
|
+
# Pyflakes errors
|
57
|
+
"F",
|
58
|
+
# isort
|
59
|
+
"I",
|
60
|
+
# flake8-simplify
|
61
|
+
"SIM",
|
62
|
+
# flake8-tidy-imports
|
63
|
+
"TID",
|
64
|
+
# pyupgrade
|
65
|
+
"UP",
|
66
|
+
# Pyflakes warnings
|
67
|
+
"W",
|
68
|
+
]
|
69
|
+
lint.ignore = [
|
70
|
+
# flake8-bugbear opinionated rules
|
71
|
+
"B9",
|
72
|
+
# line-too-long
|
73
|
+
"E501",
|
74
|
+
# suppressible-exception
|
75
|
+
"SIM105",
|
76
|
+
# if-else-block-instead-of-if-exp
|
77
|
+
"SIM108",
|
78
|
+
]
|
79
|
+
lint.extend-safe-fixes = [
|
80
|
+
# non-pep585-annotation
|
81
|
+
"UP006",
|
42
82
|
]
|
43
|
-
|
44
|
-
profile = "black"
|
83
|
+
lint.isort.required-imports = [ "from __future__ import annotations" ]
|
45
84
|
|
46
85
|
[tool.pyproject-fmt]
|
47
86
|
max_supported_python = "3.13"
|
{unittest_parametrize-1.6.0 → unittest_parametrize-1.7.0}/src/unittest_parametrize/__init__.py
RENAMED
@@ -5,9 +5,7 @@ import sys
|
|
5
5
|
from collections.abc import Sequence
|
6
6
|
from functools import wraps
|
7
7
|
from types import FunctionType
|
8
|
-
from typing import Any
|
9
|
-
from typing import Callable
|
10
|
-
from typing import TypeVar
|
8
|
+
from typing import Any, Callable, TypeVar
|
11
9
|
from unittest import TestCase
|
12
10
|
|
13
11
|
if sys.version_info >= (3, 10):
|
@@ -122,8 +120,8 @@ TestFunc = Callable[P, T]
|
|
122
120
|
|
123
121
|
def parametrize(
|
124
122
|
argnames: str | Sequence[str],
|
125
|
-
argvalues: Sequence[tuple[Any, ...]
|
126
|
-
ids: Sequence[str | None] | None = None,
|
123
|
+
argvalues: Sequence[tuple[Any, ...] | param],
|
124
|
+
ids: Sequence[str | None] | Callable[[Any], str | None] | None = None,
|
127
125
|
) -> Callable[[Callable[P, T]], Callable[P, T]]:
|
128
126
|
if isinstance(argnames, str):
|
129
127
|
argnames = [a.strip() for a in argnames.split(",")]
|
@@ -131,24 +129,22 @@ def parametrize(
|
|
131
129
|
if len(argnames) == 0:
|
132
130
|
raise ValueError("argnames must contain at least one element")
|
133
131
|
|
134
|
-
|
132
|
+
ids_callable = callable(ids)
|
133
|
+
if ids is not None and not ids_callable and len(ids) != len(argvalues): # type: ignore[arg-type]
|
135
134
|
raise ValueError("ids must have the same length as argvalues")
|
136
135
|
|
137
136
|
seen_ids = set()
|
138
137
|
params = []
|
139
138
|
for i, argvalue in enumerate(argvalues):
|
140
|
-
if ids and ids[i]:
|
141
|
-
id_ = ids[i]
|
142
|
-
else:
|
143
|
-
id_ = str(i)
|
144
|
-
|
145
139
|
if isinstance(argvalue, tuple):
|
146
140
|
if len(argvalue) != len(argnames):
|
147
141
|
raise ValueError(
|
148
142
|
f"tuple at index {i} has wrong number of arguments "
|
149
143
|
+ f"({len(argvalue)} != {len(argnames)})"
|
150
144
|
)
|
151
|
-
|
145
|
+
argvalue = param(*argvalue, id=make_id(i, argvalue, ids))
|
146
|
+
params.append(argvalue)
|
147
|
+
seen_ids.add(argvalue.id)
|
152
148
|
elif isinstance(argvalue, param):
|
153
149
|
if len(argvalue.args) != len(argnames):
|
154
150
|
raise ValueError(
|
@@ -157,7 +153,7 @@ def parametrize(
|
|
157
153
|
)
|
158
154
|
|
159
155
|
if argvalue.id is None:
|
160
|
-
argvalue = param(*argvalue.args, id=
|
156
|
+
argvalue = param(*argvalue.args, id=make_id(i, argvalue, ids))
|
161
157
|
if argvalue.id in seen_ids:
|
162
158
|
raise ValueError(f"Duplicate param id {argvalue.id!r}")
|
163
159
|
seen_ids.add(argvalue.id)
|
@@ -183,3 +179,34 @@ def parametrize(
|
|
183
179
|
return func
|
184
180
|
|
185
181
|
return wrapper
|
182
|
+
|
183
|
+
|
184
|
+
def make_id(
|
185
|
+
i: int,
|
186
|
+
argvalue: tuple[Any, ...] | param,
|
187
|
+
ids: Sequence[str | None] | Callable[[Any], str | None] | None,
|
188
|
+
) -> str:
|
189
|
+
if callable(ids):
|
190
|
+
if isinstance(argvalue, tuple):
|
191
|
+
values = argvalue
|
192
|
+
else:
|
193
|
+
values = argvalue.args
|
194
|
+
|
195
|
+
id_parts = []
|
196
|
+
for value in values:
|
197
|
+
id_part = ids(value)
|
198
|
+
if id_part is not None:
|
199
|
+
id_parts.append(id_part)
|
200
|
+
else:
|
201
|
+
id_parts.append(str(value))
|
202
|
+
id_ = "_".join(id_parts)
|
203
|
+
# Validate the generated ID
|
204
|
+
if not f"_{id_}".isidentifier():
|
205
|
+
raise ValueError(
|
206
|
+
f"callable ids returned invalid Python identifier suffix: {id_!r}"
|
207
|
+
)
|
208
|
+
return id_
|
209
|
+
elif ids and ids[i]:
|
210
|
+
return str(ids[i])
|
211
|
+
else:
|
212
|
+
return str(i)
|
{unittest_parametrize-1.6.0 → unittest_parametrize-1.7.0/src/unittest_parametrize.egg-info}/PKG-INFO
RENAMED
@@ -1,15 +1,15 @@
|
|
1
|
-
Metadata-Version: 2.
|
1
|
+
Metadata-Version: 2.4
|
2
2
|
Name: unittest-parametrize
|
3
|
-
Version: 1.
|
3
|
+
Version: 1.7.0
|
4
4
|
Summary: Parametrize tests within unittest TestCases.
|
5
5
|
Author-email: Adam Johnson <me@adamj.eu>
|
6
|
+
License-Expression: MIT
|
6
7
|
Project-URL: Changelog, https://github.com/adamchainz/unittest-parametrize/blob/main/CHANGELOG.rst
|
7
8
|
Project-URL: Funding, https://adamj.eu/books/
|
8
9
|
Project-URL: Repository, https://github.com/adamchainz/unittest-parametrize
|
9
10
|
Keywords: unittest
|
10
11
|
Classifier: Development Status :: 5 - Production/Stable
|
11
12
|
Classifier: Intended Audience :: Developers
|
12
|
-
Classifier: License :: OSI Approved :: MIT License
|
13
13
|
Classifier: Natural Language :: English
|
14
14
|
Classifier: Programming Language :: Python :: 3 :: Only
|
15
15
|
Classifier: Programming Language :: Python :: 3.9
|
@@ -22,6 +22,7 @@ Requires-Python: >=3.9
|
|
22
22
|
Description-Content-Type: text/x-rst
|
23
23
|
License-File: LICENSE
|
24
24
|
Requires-Dist: typing-extensions; python_version < "3.10"
|
25
|
+
Dynamic: license-file
|
25
26
|
|
26
27
|
====================
|
27
28
|
unittest-parametrize
|
@@ -83,8 +84,7 @@ Here’s a basic example:
|
|
83
84
|
|
84
85
|
.. code-block:: python
|
85
86
|
|
86
|
-
from unittest_parametrize import parametrize
|
87
|
-
from unittest_parametrize import ParametrizedTestCase
|
87
|
+
from unittest_parametrize import ParametrizedTestCase, parametrize
|
88
88
|
|
89
89
|
|
90
90
|
class SquareTests(ParametrizedTestCase):
|
@@ -113,8 +113,7 @@ You can provide argument names as a sequence of strings instead:
|
|
113
113
|
|
114
114
|
.. code-block:: python
|
115
115
|
|
116
|
-
from unittest_parametrize import parametrize
|
117
|
-
from unittest_parametrize import ParametrizedTestCase
|
116
|
+
from unittest_parametrize import ParametrizedTestCase, parametrize
|
118
117
|
|
119
118
|
|
120
119
|
class SquareTests(ParametrizedTestCase):
|
@@ -177,13 +176,20 @@ You can see these names when running the tests:
|
|
177
176
|
|
178
177
|
OK
|
179
178
|
|
180
|
-
You can customize these names
|
179
|
+
You can customize these names in several ways:
|
180
|
+
|
181
|
+
1. Using ``param`` objects with IDs.
|
182
|
+
2. Passing a sequence of strings as the ``ids`` argument.
|
183
|
+
3. Passing a callable as the ``ids`` argument.
|
184
|
+
|
185
|
+
Passing ``param`` objects with IDs
|
186
|
+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
187
|
+
|
188
|
+
Pass a ``param`` object for each parameter set, setting the test ID suffix with the optional ``id`` argument:
|
181
189
|
|
182
190
|
.. code-block:: python
|
183
191
|
|
184
|
-
from unittest_parametrize import param
|
185
|
-
from unittest_parametrize import parametrize
|
186
|
-
from unittest_parametrize import ParametrizedTestCase
|
192
|
+
from unittest_parametrize import ParametrizedTestCase, param, parametrize
|
187
193
|
|
188
194
|
|
189
195
|
class SquareTests(ParametrizedTestCase):
|
@@ -197,7 +203,7 @@ You can customize these names by passing ``param`` objects, which contain the ar
|
|
197
203
|
def test_square(self, x: int, expected: int) -> None:
|
198
204
|
self.assertEqual(x**2, expected)
|
199
205
|
|
200
|
-
Yielding
|
206
|
+
Yielding more natural names:
|
201
207
|
|
202
208
|
.. code-block:: console
|
203
209
|
|
@@ -216,9 +222,7 @@ Since parameter IDs are optional, you can provide them only for some tests:
|
|
216
222
|
|
217
223
|
.. code-block:: python
|
218
224
|
|
219
|
-
from unittest_parametrize import param
|
220
|
-
from unittest_parametrize import parametrize
|
221
|
-
from unittest_parametrize import ParametrizedTestCase
|
225
|
+
from unittest_parametrize import ParametrizedTestCase, param, parametrize
|
222
226
|
|
223
227
|
|
224
228
|
class SquareTests(ParametrizedTestCase):
|
@@ -232,7 +236,7 @@ Since parameter IDs are optional, you can provide them only for some tests:
|
|
232
236
|
def test_square(self, x: int, expected: int) -> None:
|
233
237
|
self.assertEqual(x**2, expected)
|
234
238
|
|
235
|
-
ID-free ``param``\s fall back to the default index suffixes:
|
239
|
+
The ID-free ``param``\s fall back to the default index suffixes:
|
236
240
|
|
237
241
|
.. code-block:: console
|
238
242
|
|
@@ -245,12 +249,14 @@ ID-free ``param``\s fall back to the default index suffixes:
|
|
245
249
|
|
246
250
|
OK
|
247
251
|
|
248
|
-
|
252
|
+
Passing a sequence of strings as the ``ids`` argument
|
253
|
+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
254
|
+
|
255
|
+
Another option is to provide the IDs in the separate ``ids`` argument:
|
249
256
|
|
250
257
|
.. code-block:: python
|
251
258
|
|
252
|
-
from unittest_parametrize import parametrize
|
253
|
-
from unittest_parametrize import ParametrizedTestCase
|
259
|
+
from unittest_parametrize import ParametrizedTestCase, parametrize
|
254
260
|
|
255
261
|
|
256
262
|
class SquareTests(ParametrizedTestCase):
|
@@ -265,6 +271,64 @@ Alternatively, you can provide the id’s separately with the ``ids`` argument:
|
|
265
271
|
def test_square(self, x: int, expected: int) -> None:
|
266
272
|
self.assertEqual(x**2, expected)
|
267
273
|
|
274
|
+
This option sets the full suffixes to the provided strings:
|
275
|
+
|
276
|
+
.. code-block:: console
|
277
|
+
|
278
|
+
$ python -m unittest t.py -v
|
279
|
+
test_square_one (example.SquareTests.test_square_one) ... ok
|
280
|
+
test_square_two (example.SquareTests.test_square_two) ... ok
|
281
|
+
|
282
|
+
----------------------------------------------------------------------
|
283
|
+
Ran 2 tests in 0.000s
|
284
|
+
|
285
|
+
OK
|
286
|
+
|
287
|
+
Passing a callable as the ``ids`` argument
|
288
|
+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
289
|
+
|
290
|
+
The ``ids`` argument can also be a callable, which unittest-parametrize calls once per parameter value.
|
291
|
+
The callable can return a string for that value, or ``None`` to use the default index suffix.
|
292
|
+
The values are then joined with underscores to form the full suffix.
|
293
|
+
|
294
|
+
For example:
|
295
|
+
|
296
|
+
.. code-block:: python
|
297
|
+
|
298
|
+
from unittest_parametrize import ParametrizedTestCase, parametrize
|
299
|
+
|
300
|
+
|
301
|
+
def make_id(value):
|
302
|
+
if isinstance(value, int):
|
303
|
+
return f"num{value}"
|
304
|
+
return None
|
305
|
+
|
306
|
+
|
307
|
+
class SquareTests(ParametrizedTestCase):
|
308
|
+
@parametrize(
|
309
|
+
"x,expected",
|
310
|
+
[
|
311
|
+
(1, 1),
|
312
|
+
(2, 4),
|
313
|
+
],
|
314
|
+
ids=make_id,
|
315
|
+
)
|
316
|
+
def test_square(self, x: int, expected: int) -> None:
|
317
|
+
self.assertEqual(x**2, expected)
|
318
|
+
|
319
|
+
…yields:
|
320
|
+
|
321
|
+
.. code-block:: console
|
322
|
+
|
323
|
+
$ python -m unittest t.py -v
|
324
|
+
test_square_num1_num1 (example.SquareTests.test_square_num1_num1) ... ok
|
325
|
+
test_square_num2_num4 (example.SquareTests.test_square_num2_num4) ... ok
|
326
|
+
|
327
|
+
----------------------------------------------------------------------
|
328
|
+
Ran 2 tests in 0.000s
|
329
|
+
|
330
|
+
OK
|
331
|
+
|
268
332
|
Use with other test decorators
|
269
333
|
------------------------------
|
270
334
|
|
@@ -275,8 +339,7 @@ So decorators like ``@mock.patch`` need be beneath ``@parametrize``:
|
|
275
339
|
.. code-block:: python
|
276
340
|
|
277
341
|
from unittest import mock
|
278
|
-
from unittest_parametrize import parametrize
|
279
|
-
from unittest_parametrize import ParametrizedTestCase
|
342
|
+
from unittest_parametrize import ParametrizedTestCase, parametrize
|
280
343
|
|
281
344
|
|
282
345
|
class CarpentryTests(ParametrizedTestCase):
|
@@ -285,8 +348,7 @@ So decorators like ``@mock.patch`` need be beneath ``@parametrize``:
|
|
285
348
|
[(11,), (17,)],
|
286
349
|
)
|
287
350
|
@mock.patch("example.hammer", autospec=True)
|
288
|
-
def test_nail_a_board(self, mock_hammer, nails):
|
289
|
-
...
|
351
|
+
def test_nail_a_board(self, mock_hammer, nails): ...
|
290
352
|
|
291
353
|
Also note that due to how ``mock.patch`` always adds positional arguments at the start, the parametrized arguments must come last.
|
292
354
|
``@parametrize`` always adds parameters as keyword arguments, so you can also use `keyword-only syntax <https://peps.python.org/pep-3102/>`__ for parametrized arguments:
|
@@ -294,8 +356,7 @@ Also note that due to how ``mock.patch`` always adds positional arguments at the
|
|
294
356
|
.. code-block:: python
|
295
357
|
|
296
358
|
# ...
|
297
|
-
def test_nail_a_board(self, mock_hammer, *, nails):
|
298
|
-
...
|
359
|
+
def test_nail_a_board(self, mock_hammer, *, nails): ...
|
299
360
|
|
300
361
|
Multiple ``@parametrize`` decorators
|
301
362
|
------------------------------------
|
@@ -305,8 +366,7 @@ To create a cross-product of tests, you can use nested list comprehensions:
|
|
305
366
|
|
306
367
|
.. code-block:: python
|
307
368
|
|
308
|
-
from unittest_parametrize import parametrize
|
309
|
-
from unittest_parametrize import ParametrizedTestCase
|
369
|
+
from unittest_parametrize import ParametrizedTestCase, parametrize
|
310
370
|
|
311
371
|
|
312
372
|
class RocketTests(ParametrizedTestCase):
|
@@ -318,8 +378,7 @@ To create a cross-product of tests, you can use nested list comprehensions:
|
|
318
378
|
for hyperdrive_level in [0, 1, 2]
|
319
379
|
],
|
320
380
|
)
|
321
|
-
def test_takeoff(self, use_ions, hyperdrive_level) -> None:
|
322
|
-
...
|
381
|
+
def test_takeoff(self, use_ions, hyperdrive_level) -> None: ...
|
323
382
|
|
324
383
|
The above creates 2 * 3 = 6 versions of ``test_takeoff``.
|
325
384
|
|
@@ -331,8 +390,7 @@ __ https://docs.python.org/3/library/itertools.html#itertools.product
|
|
331
390
|
.. code-block:: python
|
332
391
|
|
333
392
|
from itertools import product
|
334
|
-
from unittest_parametrize import parametrize
|
335
|
-
from unittest_parametrize import ParametrizedTestCase
|
393
|
+
from unittest_parametrize import ParametrizedTestCase, parametrize
|
336
394
|
|
337
395
|
|
338
396
|
class RocketTests(ParametrizedTestCase):
|
@@ -346,8 +404,7 @@ __ https://docs.python.org/3/library/itertools.html#itertools.product
|
|
346
404
|
)
|
347
405
|
),
|
348
406
|
)
|
349
|
-
def test_takeoff(self, use_ions, hyperdrive_level, nose_colour) -> None:
|
350
|
-
...
|
407
|
+
def test_takeoff(self, use_ions, hyperdrive_level, nose_colour) -> None: ...
|
351
408
|
|
352
409
|
The above creates 2 * 3 * 2 = 12 versions of ``test_takeoff``.
|
353
410
|
|
@@ -371,15 +428,47 @@ To parametrize all tests within a test case, create a separate decorator and app
|
|
371
428
|
|
372
429
|
class StatsTests(ParametrizedTestCase):
|
373
430
|
@parametrize_race
|
374
|
-
def test_strength(self, race: str) -> None:
|
375
|
-
...
|
431
|
+
def test_strength(self, race: str) -> None: ...
|
376
432
|
|
377
433
|
@parametrize_race
|
378
|
-
def test_dexterity(self, race: str) -> None:
|
379
|
-
...
|
434
|
+
def test_dexterity(self, race: str) -> None: ...
|
380
435
|
|
381
436
|
...
|
382
437
|
|
438
|
+
Pass parameters in a dataclass
|
439
|
+
------------------------------
|
440
|
+
|
441
|
+
Thanks to `Florian Bruhin <https://bruhin.software/>`__ for this tip, from his `pytest tips and tricks presentation <https://bruhin.software/>`__.
|
442
|
+
|
443
|
+
If your test uses many parameters or cases, the parametrization may become unwieldy, as cases don’t name the arguments.
|
444
|
+
In this case, try using a `dataclass <https://docs.python.org/3/library/dataclasses.html>`__ to hold the arguments:
|
445
|
+
|
446
|
+
.. code-block:: python
|
447
|
+
|
448
|
+
from dataclasses import dataclass
|
449
|
+
|
450
|
+
from unittest_parametrize import ParametrizedTestCase, parametrize
|
451
|
+
|
452
|
+
|
453
|
+
@dataclass
|
454
|
+
class SquareParams:
|
455
|
+
x: int
|
456
|
+
expected: int
|
457
|
+
|
458
|
+
|
459
|
+
class SquareTests(ParametrizedTestCase):
|
460
|
+
@parametrize(
|
461
|
+
"sp",
|
462
|
+
[
|
463
|
+
(SquareParams(x=1, expected=1),),
|
464
|
+
(SquareParams(x=2, expected=4),),
|
465
|
+
],
|
466
|
+
)
|
467
|
+
def test_square(self, sp: SquareParams) -> None:
|
468
|
+
self.assertEqual(sp.x**2, sp.expected)
|
469
|
+
|
470
|
+
This way, each parameter is type-checked and named, improving safety and readability.
|
471
|
+
|
383
472
|
History
|
384
473
|
=======
|
385
474
|
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|