METADATA 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683
  1. Metadata-Version: 2.1
  2. Name: parameterized
  3. Version: 0.8.1
  4. Summary: Parameterized testing with any Python test framework
  5. Home-page: https://github.com/wolever/parameterized
  6. Author: David Wolever
  7. Author-email: david@wolever.net
  8. License: FreeBSD
  9. Platform: UNKNOWN
  10. Classifier: Programming Language :: Python
  11. Classifier: Programming Language :: Python :: 3
  12. Classifier: License :: OSI Approved :: BSD License
  13. Provides-Extra: dev
  14. Requires-Dist: jinja2 ; extra == 'dev'
  15. Parameterized testing with any Python test framework
  16. ====================================================
  17. .. image:: https://img.shields.io/pypi/v/parameterized.svg
  18. :alt: PyPI
  19. :target: https://pypi.org/project/parameterized/
  20. .. image:: https://circleci.com/gh/wolever/parameterized.svg?style=svg
  21. :alt: Circle CI
  22. :target: https://circleci.com/gh/wolever/parameterized
  23. Parameterized testing in Python sucks.
  24. ``parameterized`` fixes that. For everything. Parameterized testing for nose,
  25. parameterized testing for py.test, parameterized testing for unittest.
  26. .. code:: python
  27. # test_math.py
  28. from nose.tools import assert_equal
  29. from parameterized import parameterized, parameterized_class
  30. import unittest
  31. import math
  32. @parameterized([
  33. (2, 2, 4),
  34. (2, 3, 8),
  35. (1, 9, 1),
  36. (0, 9, 0),
  37. ])
  38. def test_pow(base, exponent, expected):
  39. assert_equal(math.pow(base, exponent), expected)
  40. class TestMathUnitTest(unittest.TestCase):
  41. @parameterized.expand([
  42. ("negative", -1.5, -2.0),
  43. ("integer", 1, 1.0),
  44. ("large fraction", 1.6, 1),
  45. ])
  46. def test_floor(self, name, input, expected):
  47. assert_equal(math.floor(input), expected)
  48. @parameterized_class(('a', 'b', 'expected_sum', 'expected_product'), [
  49. (1, 2, 3, 2),
  50. (5, 5, 10, 25),
  51. ])
  52. class TestMathClass(unittest.TestCase):
  53. def test_add(self):
  54. assert_equal(self.a + self.b, self.expected_sum)
  55. def test_multiply(self):
  56. assert_equal(self.a * self.b, self.expected_product)
  57. @parameterized_class([
  58. { "a": 3, "expected": 2 },
  59. { "b": 5, "expected": -4 },
  60. ])
  61. class TestMathClassDict(unittest.TestCase):
  62. a = 1
  63. b = 1
  64. def test_subtract(self):
  65. assert_equal(self.a - self.b, self.expected)
  66. With nose (and nose2)::
  67. $ nosetests -v test_math.py
  68. test_floor_0_negative (test_math.TestMathUnitTest) ... ok
  69. test_floor_1_integer (test_math.TestMathUnitTest) ... ok
  70. test_floor_2_large_fraction (test_math.TestMathUnitTest) ... ok
  71. test_math.test_pow(2, 2, 4, {}) ... ok
  72. test_math.test_pow(2, 3, 8, {}) ... ok
  73. test_math.test_pow(1, 9, 1, {}) ... ok
  74. test_math.test_pow(0, 9, 0, {}) ... ok
  75. test_add (test_math.TestMathClass_0) ... ok
  76. test_multiply (test_math.TestMathClass_0) ... ok
  77. test_add (test_math.TestMathClass_1) ... ok
  78. test_multiply (test_math.TestMathClass_1) ... ok
  79. test_subtract (test_math.TestMathClassDict_0) ... ok
  80. ----------------------------------------------------------------------
  81. Ran 12 tests in 0.015s
  82. OK
  83. As the package name suggests, nose is best supported and will be used for all
  84. further examples.
  85. With py.test (version 2.0 and above)::
  86. $ py.test -v test_math.py
  87. ============================= test session starts ==============================
  88. platform darwin -- Python 3.6.1, pytest-3.1.3, py-1.4.34, pluggy-0.4.0
  89. collecting ... collected 13 items
  90. test_math.py::test_pow::[0] PASSED
  91. test_math.py::test_pow::[1] PASSED
  92. test_math.py::test_pow::[2] PASSED
  93. test_math.py::test_pow::[3] PASSED
  94. test_math.py::TestMathUnitTest::test_floor_0_negative PASSED
  95. test_math.py::TestMathUnitTest::test_floor_1_integer PASSED
  96. test_math.py::TestMathUnitTest::test_floor_2_large_fraction PASSED
  97. test_math.py::TestMathClass_0::test_add PASSED
  98. test_math.py::TestMathClass_0::test_multiply PASSED
  99. test_math.py::TestMathClass_1::test_add PASSED
  100. test_math.py::TestMathClass_1::test_multiply PASSED
  101. test_math.py::TestMathClassDict_0::test_subtract PASSED
  102. ==================== 12 passed, 4 warnings in 0.16 seconds =====================
  103. With unittest (and unittest2)::
  104. $ python -m unittest -v test_math
  105. test_floor_0_negative (test_math.TestMathUnitTest) ... ok
  106. test_floor_1_integer (test_math.TestMathUnitTest) ... ok
  107. test_floor_2_large_fraction (test_math.TestMathUnitTest) ... ok
  108. test_add (test_math.TestMathClass_0) ... ok
  109. test_multiply (test_math.TestMathClass_0) ... ok
  110. test_add (test_math.TestMathClass_1) ... ok
  111. test_multiply (test_math.TestMathClass_1) ... ok
  112. test_subtract (test_math.TestMathClassDict_0) ... ok
  113. ----------------------------------------------------------------------
  114. Ran 8 tests in 0.001s
  115. OK
  116. (note: because unittest does not support test decorators, only tests created
  117. with ``@parameterized.expand`` will be executed)
  118. With green::
  119. $ green test_math.py -vvv
  120. test_math
  121. TestMathClass_1
  122. . test_method_a
  123. . test_method_b
  124. TestMathClass_2
  125. . test_method_a
  126. . test_method_b
  127. TestMathClass_3
  128. . test_method_a
  129. . test_method_b
  130. TestMathUnitTest
  131. . test_floor_0_negative
  132. . test_floor_1_integer
  133. . test_floor_2_large_fraction
  134. TestMathClass_0
  135. . test_add
  136. . test_multiply
  137. TestMathClass_1
  138. . test_add
  139. . test_multiply
  140. TestMathClassDict_0
  141. . test_subtract
  142. Ran 12 tests in 0.121s
  143. OK (passes=9)
  144. Installation
  145. ------------
  146. ::
  147. $ pip install parameterized
  148. Compatibility
  149. -------------
  150. `Yes`__ (mostly).
  151. __ https://travis-ci.org/wolever/parameterized
  152. .. list-table::
  153. :header-rows: 1
  154. :stub-columns: 1
  155. * -
  156. - Py2.6
  157. - Py2.7
  158. - Py3.4
  159. - Py3.5
  160. - Py3.6
  161. - Py3.7
  162. - Py3.8
  163. - Py3.9
  164. - PyPy
  165. - ``@mock.patch``
  166. * - nose
  167. - yes
  168. - yes
  169. - yes
  170. - yes
  171. - yes
  172. - yes
  173. - yes
  174. - yes
  175. - yes
  176. - yes
  177. * - nose2
  178. - yes
  179. - yes
  180. - yes
  181. - yes
  182. - yes
  183. - yes
  184. - yes
  185. - yes
  186. - yes
  187. - yes
  188. * - py.test 2
  189. - yes
  190. - yes
  191. - no*
  192. - no*
  193. - no*
  194. - no*
  195. - yes
  196. - yes
  197. - yes
  198. - yes
  199. * - py.test 3
  200. - yes
  201. - yes
  202. - yes
  203. - yes
  204. - yes
  205. - yes
  206. - yes
  207. - yes
  208. - yes
  209. - yes
  210. * - py.test 4
  211. - no**
  212. - no**
  213. - no**
  214. - no**
  215. - no**
  216. - no**
  217. - no**
  218. - no**
  219. - no**
  220. - no**
  221. * - py.test fixtures
  222. - no†
  223. - no†
  224. - no†
  225. - no†
  226. - no†
  227. - no†
  228. - no†
  229. - no†
  230. - no†
  231. - no†
  232. * - | unittest
  233. | (``@parameterized.expand``)
  234. - yes
  235. - yes
  236. - yes
  237. - yes
  238. - yes
  239. - yes
  240. - yes
  241. - yes
  242. - yes
  243. - yes
  244. * - | unittest2
  245. | (``@parameterized.expand``)
  246. - yes
  247. - yes
  248. - yes
  249. - yes
  250. - yes
  251. - yes
  252. - yes
  253. - yes
  254. - yes
  255. - yes
  256. \*: py.test 2 does `does not appear to work (#71)`__ under Python 3. Please comment on the related issues if you are affected.
  257. \*\*: py.test 4 is not yet supported (but coming!) in `issue #34`__
  258. †: py.test fixture support is documented in `issue #81`__
  259. __ https://github.com/wolever/parameterized/issues/71
  260. __ https://github.com/wolever/parameterized/issues/34
  261. __ https://github.com/wolever/parameterized/issues/81
  262. Dependencies
  263. ------------
  264. (this section left intentionally blank)
  265. Exhaustive Usage Examples
  266. --------------------------
  267. The ``@parameterized`` and ``@parameterized.expand`` decorators accept a list
  268. or iterable of tuples or ``param(...)``, or a callable which returns a list or
  269. iterable:
  270. .. code:: python
  271. from parameterized import parameterized, param
  272. # A list of tuples
  273. @parameterized([
  274. (2, 3, 5),
  275. (3, 5, 8),
  276. ])
  277. def test_add(a, b, expected):
  278. assert_equal(a + b, expected)
  279. # A list of params
  280. @parameterized([
  281. param("10", 10),
  282. param("10", 16, base=16),
  283. ])
  284. def test_int(str_val, expected, base=10):
  285. assert_equal(int(str_val, base=base), expected)
  286. # An iterable of params
  287. @parameterized(
  288. param.explicit(*json.loads(line))
  289. for line in open("testcases.jsons")
  290. )
  291. def test_from_json_file(...):
  292. ...
  293. # A callable which returns a list of tuples
  294. def load_test_cases():
  295. return [
  296. ("test1", ),
  297. ("test2", ),
  298. ]
  299. @parameterized(load_test_cases)
  300. def test_from_function(name):
  301. ...
  302. .. **
  303. Note that, when using an iterator or a generator, all the items will be loaded
  304. into memory before the start of the test run (we do this explicitly to ensure
  305. that generators are exhausted exactly once in multi-process or multi-threaded
  306. testing environments).
  307. The ``@parameterized`` decorator can be used test class methods, and standalone
  308. functions:
  309. .. code:: python
  310. from parameterized import parameterized
  311. class AddTest(object):
  312. @parameterized([
  313. (2, 3, 5),
  314. ])
  315. def test_add(self, a, b, expected):
  316. assert_equal(a + b, expected)
  317. @parameterized([
  318. (2, 3, 5),
  319. ])
  320. def test_add(a, b, expected):
  321. assert_equal(a + b, expected)
  322. And ``@parameterized.expand`` can be used to generate test methods in
  323. situations where test generators cannot be used (for example, when the test
  324. class is a subclass of ``unittest.TestCase``):
  325. .. code:: python
  326. import unittest
  327. from parameterized import parameterized
  328. class AddTestCase(unittest.TestCase):
  329. @parameterized.expand([
  330. ("2 and 3", 2, 3, 5),
  331. ("3 and 5", 2, 3, 5),
  332. ])
  333. def test_add(self, _, a, b, expected):
  334. assert_equal(a + b, expected)
  335. Will create the test cases::
  336. $ nosetests example.py
  337. test_add_0_2_and_3 (example.AddTestCase) ... ok
  338. test_add_1_3_and_5 (example.AddTestCase) ... ok
  339. ----------------------------------------------------------------------
  340. Ran 2 tests in 0.001s
  341. OK
  342. Note that ``@parameterized.expand`` works by creating new methods on the test
  343. class. If the first parameter is a string, that string will be added to the end
  344. of the method name. For example, the test case above will generate the methods
  345. ``test_add_0_2_and_3`` and ``test_add_1_3_and_5``.
  346. The names of the test cases generated by ``@parameterized.expand`` can be
  347. customized using the ``name_func`` keyword argument. The value should
  348. be a function which accepts three arguments: ``testcase_func``, ``param_num``,
  349. and ``params``, and it should return the name of the test case.
  350. ``testcase_func`` will be the function to be tested, ``param_num`` will be the
  351. index of the test case parameters in the list of parameters, and ``param``
  352. (an instance of ``param``) will be the parameters which will be used.
  353. .. code:: python
  354. import unittest
  355. from parameterized import parameterized
  356. def custom_name_func(testcase_func, param_num, param):
  357. return "%s_%s" %(
  358. testcase_func.__name__,
  359. parameterized.to_safe_name("_".join(str(x) for x in param.args)),
  360. )
  361. class AddTestCase(unittest.TestCase):
  362. @parameterized.expand([
  363. (2, 3, 5),
  364. (2, 3, 5),
  365. ], name_func=custom_name_func)
  366. def test_add(self, a, b, expected):
  367. assert_equal(a + b, expected)
  368. Will create the test cases::
  369. $ nosetests example.py
  370. test_add_1_2_3 (example.AddTestCase) ... ok
  371. test_add_2_3_5 (example.AddTestCase) ... ok
  372. ----------------------------------------------------------------------
  373. Ran 2 tests in 0.001s
  374. OK
  375. The ``param(...)`` helper class stores the parameters for one specific test
  376. case. It can be used to pass keyword arguments to test cases:
  377. .. code:: python
  378. from parameterized import parameterized, param
  379. @parameterized([
  380. param("10", 10),
  381. param("10", 16, base=16),
  382. ])
  383. def test_int(str_val, expected, base=10):
  384. assert_equal(int(str_val, base=base), expected)
  385. If test cases have a docstring, the parameters for that test case will be
  386. appended to the first line of the docstring. This behavior can be controlled
  387. with the ``doc_func`` argument:
  388. .. code:: python
  389. from parameterized import parameterized
  390. @parameterized([
  391. (1, 2, 3),
  392. (4, 5, 9),
  393. ])
  394. def test_add(a, b, expected):
  395. """ Test addition. """
  396. assert_equal(a + b, expected)
  397. def my_doc_func(func, num, param):
  398. return "%s: %s with %s" %(num, func.__name__, param)
  399. @parameterized([
  400. (5, 4, 1),
  401. (9, 6, 3),
  402. ], doc_func=my_doc_func)
  403. def test_subtraction(a, b, expected):
  404. assert_equal(a - b, expected)
  405. ::
  406. $ nosetests example.py
  407. Test addition. [with a=1, b=2, expected=3] ... ok
  408. Test addition. [with a=4, b=5, expected=9] ... ok
  409. 0: test_subtraction with param(*(5, 4, 1)) ... ok
  410. 1: test_subtraction with param(*(9, 6, 3)) ... ok
  411. ----------------------------------------------------------------------
  412. Ran 4 tests in 0.001s
  413. OK
  414. Finally ``@parameterized_class`` parameterizes an entire class, using
  415. either a list of attributes, or a list of dicts that will be applied to the
  416. class:
  417. .. code:: python
  418. from yourapp.models import User
  419. from parameterized import parameterized_class
  420. @parameterized_class([
  421. { "username": "user_1", "access_level": 1 },
  422. { "username": "user_2", "access_level": 2, "expected_status_code": 404 },
  423. ])
  424. class TestUserAccessLevel(TestCase):
  425. expected_status_code = 200
  426. def setUp(self):
  427. self.client.force_login(User.objects.get(username=self.username)[0])
  428. def test_url_a(self):
  429. response = self.client.get('/url')
  430. self.assertEqual(response.status_code, self.expected_status_code)
  431. def tearDown(self):
  432. self.client.logout()
  433. @parameterized_class(("username", "access_level", "expected_status_code"), [
  434. ("user_1", 1, 200),
  435. ("user_2", 2, 404)
  436. ])
  437. class TestUserAccessLevel(TestCase):
  438. def setUp(self):
  439. self.client.force_login(User.objects.get(username=self.username)[0])
  440. def test_url_a(self):
  441. response = self.client.get("/url")
  442. self.assertEqual(response.status_code, self.expected_status_code)
  443. def tearDown(self):
  444. self.client.logout()
  445. The ``@parameterized_class`` decorator accepts a ``class_name_func`` argument,
  446. which controls the name of the parameterized classes generated by
  447. ``@parameterized_class``:
  448. .. code:: python
  449. from parameterized import parameterized, parameterized_class
  450. def get_class_name(cls, num, params_dict):
  451. # By default the generated class named includes either the "name"
  452. # parameter (if present), or the first string value. This example shows
  453. # multiple parameters being included in the generated class name:
  454. return "%s_%s_%s%s" %(
  455. cls.__name__,
  456. num,
  457. parameterized.to_safe_name(params_dict['a']),
  458. parameterized.to_safe_name(params_dict['b']),
  459. )
  460. @parameterized_class([
  461. { "a": "hello", "b": " world!", "expected": "hello world!" },
  462. { "a": "say ", "b": " cheese :)", "expected": "say cheese :)" },
  463. ], class_name_func=get_class_name)
  464. class TestConcatenation(TestCase):
  465. def test_concat(self):
  466. self.assertEqual(self.a + self.b, self.expected)
  467. ::
  468. $ nosetests -v test_math.py
  469. test_concat (test_concat.TestConcatenation_0_hello_world_) ... ok
  470. test_concat (test_concat.TestConcatenation_0_say_cheese__) ... ok
  471. Using with Single Parameters
  472. ............................
  473. If a test function only accepts one parameter and the value is not iterable,
  474. then it is possible to supply a list of values without wrapping each one in a
  475. tuple:
  476. .. code:: python
  477. @parameterized([1, 2, 3])
  478. def test_greater_than_zero(value):
  479. assert value > 0
  480. Note, however, that if the single parameter *is* iterable (such as a list or
  481. tuple), then it *must* be wrapped in a tuple, list, or the ``param(...)``
  482. helper:
  483. .. code:: python
  484. @parameterized([
  485. ([1, 2, 3], ),
  486. ([3, 3], ),
  487. ([6], ),
  488. ])
  489. def test_sums_to_6(numbers):
  490. assert sum(numbers) == 6
  491. (note, also, that Python requires single element tuples to be defined with a
  492. trailing comma: ``(foo, )``)
  493. Using with ``@mock.patch``
  494. ..........................
  495. ``parameterized`` can be used with ``mock.patch``, but the argument ordering
  496. can be confusing. The ``@mock.patch(...)`` decorator must come *below* the
  497. ``@parameterized(...)``, and the mocked parameters must come *last*:
  498. .. code:: python
  499. @mock.patch("os.getpid")
  500. class TestOS(object):
  501. @parameterized(...)
  502. @mock.patch("os.fdopen")
  503. @mock.patch("os.umask")
  504. def test_method(self, param1, param2, ..., mock_umask, mock_fdopen, mock_getpid):
  505. ...
  506. Note: the same holds true when using ``@parameterized.expand``.
  507. Migrating from ``nose-parameterized`` to ``parameterized``
  508. ----------------------------------------------------------
  509. To migrate a codebase from ``nose-parameterized`` to ``parameterized``:
  510. 1. Update your requirements file, replacing ``nose-parameterized`` with
  511. ``parameterized``.
  512. 2. Replace all references to ``nose_parameterized`` with ``parameterized``::
  513. $ perl -pi -e 's/nose_parameterized/parameterized/g' your-codebase/
  514. 3. You're done!
  515. FAQ
  516. ---
  517. What happened to ``nose-parameterized``?
  518. Originally only nose was supported. But now everything is supported, and it
  519. only made sense to change the name!
  520. What do you mean when you say "nose is best supported"?
  521. There are small caveates with ``py.test`` and ``unittest``: ``py.test``
  522. does not show the parameter values (ex, it will show ``test_add[0]``
  523. instead of ``test_add[1, 2, 3]``), and ``unittest``/``unittest2`` do not
  524. support test generators so ``@parameterized.expand`` must be used.
  525. Why not use ``@pytest.mark.parametrize``?
  526. Because spelling is difficult. Also, ``parameterized`` doesn't require you
  527. to repeat argument names, and (using ``param``) it supports optional
  528. keyword arguments.
  529. Why do I get an ``AttributeError: 'function' object has no attribute 'expand'`` with ``@parameterized.expand``?
  530. You've likely installed the ``parametrized`` (note the missing *e*)
  531. package. Use ``parameterized`` (with the *e*) instead and you'll be all
  532. set.