aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/python/parameterized/py2/README.rst
diff options
context:
space:
mode:
authornkozlovskiy <nmk@ydb.tech>2023-09-29 12:24:06 +0300
committernkozlovskiy <nmk@ydb.tech>2023-09-29 12:41:34 +0300
commite0e3e1717e3d33762ce61950504f9637a6e669ed (patch)
treebca3ff6939b10ed60c3d5c12439963a1146b9711 /contrib/python/parameterized/py2/README.rst
parent38f2c5852db84c7b4d83adfcb009eb61541d1ccd (diff)
downloadydb-e0e3e1717e3d33762ce61950504f9637a6e669ed.tar.gz
add ydb deps
Diffstat (limited to 'contrib/python/parameterized/py2/README.rst')
-rw-r--r--contrib/python/parameterized/py2/README.rst666
1 files changed, 666 insertions, 0 deletions
diff --git a/contrib/python/parameterized/py2/README.rst b/contrib/python/parameterized/py2/README.rst
new file mode 100644
index 0000000000..4da8c05f16
--- /dev/null
+++ b/contrib/python/parameterized/py2/README.rst
@@ -0,0 +1,666 @@
+Parameterized testing with any Python test framework
+====================================================
+
+.. image:: https://img.shields.io/pypi/v/parameterized.svg
+ :alt: PyPI
+ :target: https://pypi.org/project/parameterized/
+
+.. image:: https://circleci.com/gh/wolever/parameterized.svg?style=svg
+ :alt: Circle CI
+ :target: https://circleci.com/gh/wolever/parameterized
+
+
+Parameterized testing in Python sucks.
+
+``parameterized`` fixes that. For everything. Parameterized testing for nose,
+parameterized testing for py.test, parameterized testing for unittest.
+
+.. code:: python
+
+ # test_math.py
+ from nose.tools import assert_equal
+ from parameterized import parameterized, parameterized_class
+
+ import unittest
+ import math
+
+ @parameterized([
+ (2, 2, 4),
+ (2, 3, 8),
+ (1, 9, 1),
+ (0, 9, 0),
+ ])
+ def test_pow(base, exponent, expected):
+ assert_equal(math.pow(base, exponent), expected)
+
+ class TestMathUnitTest(unittest.TestCase):
+ @parameterized.expand([
+ ("negative", -1.5, -2.0),
+ ("integer", 1, 1.0),
+ ("large fraction", 1.6, 1),
+ ])
+ def test_floor(self, name, input, expected):
+ assert_equal(math.floor(input), expected)
+
+ @parameterized_class(('a', 'b', 'expected_sum', 'expected_product'), [
+ (1, 2, 3, 2),
+ (5, 5, 10, 25),
+ ])
+ class TestMathClass(unittest.TestCase):
+ def test_add(self):
+ assert_equal(self.a + self.b, self.expected_sum)
+
+ def test_multiply(self):
+ assert_equal(self.a * self.b, self.expected_product)
+
+ @parameterized_class([
+ { "a": 3, "expected": 2 },
+ { "b": 5, "expected": -4 },
+ ])
+ class TestMathClassDict(unittest.TestCase):
+ a = 1
+ b = 1
+
+ def test_subtract(self):
+ assert_equal(self.a - self.b, self.expected)
+
+
+With nose (and nose2)::
+
+ $ nosetests -v test_math.py
+ test_floor_0_negative (test_math.TestMathUnitTest) ... ok
+ test_floor_1_integer (test_math.TestMathUnitTest) ... ok
+ test_floor_2_large_fraction (test_math.TestMathUnitTest) ... ok
+ test_math.test_pow(2, 2, 4, {}) ... ok
+ test_math.test_pow(2, 3, 8, {}) ... ok
+ test_math.test_pow(1, 9, 1, {}) ... ok
+ test_math.test_pow(0, 9, 0, {}) ... ok
+ test_add (test_math.TestMathClass_0) ... ok
+ test_multiply (test_math.TestMathClass_0) ... ok
+ test_add (test_math.TestMathClass_1) ... ok
+ test_multiply (test_math.TestMathClass_1) ... ok
+ test_subtract (test_math.TestMathClassDict_0) ... ok
+
+ ----------------------------------------------------------------------
+ Ran 12 tests in 0.015s
+
+ OK
+
+As the package name suggests, nose is best supported and will be used for all
+further examples.
+
+
+With py.test (version 2.0 and above)::
+
+ $ py.test -v test_math.py
+ ============================= test session starts ==============================
+ platform darwin -- Python 3.6.1, pytest-3.1.3, py-1.4.34, pluggy-0.4.0
+ collecting ... collected 13 items
+
+ test_math.py::test_pow::[0] PASSED
+ test_math.py::test_pow::[1] PASSED
+ test_math.py::test_pow::[2] PASSED
+ test_math.py::test_pow::[3] PASSED
+ test_math.py::TestMathUnitTest::test_floor_0_negative PASSED
+ test_math.py::TestMathUnitTest::test_floor_1_integer PASSED
+ test_math.py::TestMathUnitTest::test_floor_2_large_fraction PASSED
+ test_math.py::TestMathClass_0::test_add PASSED
+ test_math.py::TestMathClass_0::test_multiply PASSED
+ test_math.py::TestMathClass_1::test_add PASSED
+ test_math.py::TestMathClass_1::test_multiply PASSED
+ test_math.py::TestMathClassDict_0::test_subtract PASSED
+ ==================== 12 passed, 4 warnings in 0.16 seconds =====================
+
+With unittest (and unittest2)::
+
+ $ python -m unittest -v test_math
+ test_floor_0_negative (test_math.TestMathUnitTest) ... ok
+ test_floor_1_integer (test_math.TestMathUnitTest) ... ok
+ test_floor_2_large_fraction (test_math.TestMathUnitTest) ... ok
+ test_add (test_math.TestMathClass_0) ... ok
+ test_multiply (test_math.TestMathClass_0) ... ok
+ test_add (test_math.TestMathClass_1) ... ok
+ test_multiply (test_math.TestMathClass_1) ... ok
+ test_subtract (test_math.TestMathClassDict_0) ... ok
+
+ ----------------------------------------------------------------------
+ Ran 8 tests in 0.001s
+
+ OK
+
+(note: because unittest does not support test decorators, only tests created
+with ``@parameterized.expand`` will be executed)
+
+With green::
+
+ $ green test_math.py -vvv
+ test_math
+ TestMathClass_1
+ . test_method_a
+ . test_method_b
+ TestMathClass_2
+ . test_method_a
+ . test_method_b
+ TestMathClass_3
+ . test_method_a
+ . test_method_b
+ TestMathUnitTest
+ . test_floor_0_negative
+ . test_floor_1_integer
+ . test_floor_2_large_fraction
+ TestMathClass_0
+ . test_add
+ . test_multiply
+ TestMathClass_1
+ . test_add
+ . test_multiply
+ TestMathClassDict_0
+ . test_subtract
+
+ Ran 12 tests in 0.121s
+
+ OK (passes=9)
+
+
+Installation
+------------
+
+::
+
+ $ pip install parameterized
+
+
+Compatibility
+-------------
+
+`Yes`__ (mostly).
+
+__ https://travis-ci.org/wolever/parameterized
+
+.. list-table::
+ :header-rows: 1
+ :stub-columns: 1
+
+ * -
+ - Py2.6
+ - Py2.7
+ - Py3.4
+ - Py3.5
+ - Py3.6
+ - Py3.7
+ - Py3.8
+ - Py3.9
+ - PyPy
+ - ``@mock.patch``
+ * - nose
+ - yes
+ - yes
+ - yes
+ - yes
+ - yes
+ - yes
+ - yes
+ - yes
+ - yes
+ - yes
+ * - nose2
+ - yes
+ - yes
+ - yes
+ - yes
+ - yes
+ - yes
+ - yes
+ - yes
+ - yes
+ - yes
+ * - py.test 2
+ - yes
+ - yes
+ - no*
+ - no*
+ - no*
+ - no*
+ - yes
+ - yes
+ - yes
+ - yes
+ * - py.test 3
+ - yes
+ - yes
+ - yes
+ - yes
+ - yes
+ - yes
+ - yes
+ - yes
+ - yes
+ - yes
+ * - py.test 4
+ - no**
+ - no**
+ - no**
+ - no**
+ - no**
+ - no**
+ - no**
+ - no**
+ - no**
+ - no**
+ * - py.test fixtures
+ - no†
+ - no†
+ - no†
+ - no†
+ - no†
+ - no†
+ - no†
+ - no†
+ - no†
+ - no†
+ * - | unittest
+ | (``@parameterized.expand``)
+ - yes
+ - yes
+ - yes
+ - yes
+ - yes
+ - yes
+ - yes
+ - yes
+ - yes
+ - yes
+ * - | unittest2
+ | (``@parameterized.expand``)
+ - yes
+ - yes
+ - yes
+ - yes
+ - yes
+ - yes
+ - yes
+ - yes
+ - yes
+ - yes
+
+\*: py.test 2 does `does not appear to work (#71)`__ under Python 3. Please comment on the related issues if you are affected.
+
+\*\*: py.test 4 is not yet supported (but coming!) in `issue #34`__
+
+†: py.test fixture support is documented in `issue #81`__
+
+__ https://github.com/wolever/parameterized/issues/71
+__ https://github.com/wolever/parameterized/issues/34
+__ https://github.com/wolever/parameterized/issues/81
+
+Dependencies
+------------
+
+(this section left intentionally blank)
+
+
+Exhaustive Usage Examples
+--------------------------
+
+The ``@parameterized`` and ``@parameterized.expand`` decorators accept a list
+or iterable of tuples or ``param(...)``, or a callable which returns a list or
+iterable:
+
+.. code:: python
+
+ from parameterized import parameterized, param
+
+ # A list of tuples
+ @parameterized([
+ (2, 3, 5),
+ (3, 5, 8),
+ ])
+ def test_add(a, b, expected):
+ assert_equal(a + b, expected)
+
+ # A list of params
+ @parameterized([
+ param("10", 10),
+ param("10", 16, base=16),
+ ])
+ def test_int(str_val, expected, base=10):
+ assert_equal(int(str_val, base=base), expected)
+
+ # An iterable of params
+ @parameterized(
+ param.explicit(*json.loads(line))
+ for line in open("testcases.jsons")
+ )
+ def test_from_json_file(...):
+ ...
+
+ # A callable which returns a list of tuples
+ def load_test_cases():
+ return [
+ ("test1", ),
+ ("test2", ),
+ ]
+ @parameterized(load_test_cases)
+ def test_from_function(name):
+ ...
+
+.. **
+
+Note that, when using an iterator or a generator, all the items will be loaded
+into memory before the start of the test run (we do this explicitly to ensure
+that generators are exhausted exactly once in multi-process or multi-threaded
+testing environments).
+
+The ``@parameterized`` decorator can be used test class methods, and standalone
+functions:
+
+.. code:: python
+
+ from parameterized import parameterized
+
+ class AddTest(object):
+ @parameterized([
+ (2, 3, 5),
+ ])
+ def test_add(self, a, b, expected):
+ assert_equal(a + b, expected)
+
+ @parameterized([
+ (2, 3, 5),
+ ])
+ def test_add(a, b, expected):
+ assert_equal(a + b, expected)
+
+
+And ``@parameterized.expand`` can be used to generate test methods in
+situations where test generators cannot be used (for example, when the test
+class is a subclass of ``unittest.TestCase``):
+
+.. code:: python
+
+ import unittest
+ from parameterized import parameterized
+
+ class AddTestCase(unittest.TestCase):
+ @parameterized.expand([
+ ("2 and 3", 2, 3, 5),
+ ("3 and 5", 2, 3, 5),
+ ])
+ def test_add(self, _, a, b, expected):
+ assert_equal(a + b, expected)
+
+Will create the test cases::
+
+ $ nosetests example.py
+ test_add_0_2_and_3 (example.AddTestCase) ... ok
+ test_add_1_3_and_5 (example.AddTestCase) ... ok
+
+ ----------------------------------------------------------------------
+ Ran 2 tests in 0.001s
+
+ OK
+
+Note that ``@parameterized.expand`` works by creating new methods on the test
+class. If the first parameter is a string, that string will be added to the end
+of the method name. For example, the test case above will generate the methods
+``test_add_0_2_and_3`` and ``test_add_1_3_and_5``.
+
+The names of the test cases generated by ``@parameterized.expand`` can be
+customized using the ``name_func`` keyword argument. The value should
+be a function which accepts three arguments: ``testcase_func``, ``param_num``,
+and ``params``, and it should return the name of the test case.
+``testcase_func`` will be the function to be tested, ``param_num`` will be the
+index of the test case parameters in the list of parameters, and ``param``
+(an instance of ``param``) will be the parameters which will be used.
+
+.. code:: python
+
+ import unittest
+ from parameterized import parameterized
+
+ def custom_name_func(testcase_func, param_num, param):
+ return "%s_%s" %(
+ testcase_func.__name__,
+ parameterized.to_safe_name("_".join(str(x) for x in param.args)),
+ )
+
+ class AddTestCase(unittest.TestCase):
+ @parameterized.expand([
+ (2, 3, 5),
+ (2, 3, 5),
+ ], name_func=custom_name_func)
+ def test_add(self, a, b, expected):
+ assert_equal(a + b, expected)
+
+Will create the test cases::
+
+ $ nosetests example.py
+ test_add_1_2_3 (example.AddTestCase) ... ok
+ test_add_2_3_5 (example.AddTestCase) ... ok
+
+ ----------------------------------------------------------------------
+ Ran 2 tests in 0.001s
+
+ OK
+
+
+The ``param(...)`` helper class stores the parameters for one specific test
+case. It can be used to pass keyword arguments to test cases:
+
+.. code:: python
+
+ from parameterized import parameterized, param
+
+ @parameterized([
+ param("10", 10),
+ param("10", 16, base=16),
+ ])
+ def test_int(str_val, expected, base=10):
+ assert_equal(int(str_val, base=base), expected)
+
+
+If test cases have a docstring, the parameters for that test case will be
+appended to the first line of the docstring. This behavior can be controlled
+with the ``doc_func`` argument:
+
+.. code:: python
+
+ from parameterized import parameterized
+
+ @parameterized([
+ (1, 2, 3),
+ (4, 5, 9),
+ ])
+ def test_add(a, b, expected):
+ """ Test addition. """
+ assert_equal(a + b, expected)
+
+ def my_doc_func(func, num, param):
+ return "%s: %s with %s" %(num, func.__name__, param)
+
+ @parameterized([
+ (5, 4, 1),
+ (9, 6, 3),
+ ], doc_func=my_doc_func)
+ def test_subtraction(a, b, expected):
+ assert_equal(a - b, expected)
+
+::
+
+ $ nosetests example.py
+ Test addition. [with a=1, b=2, expected=3] ... ok
+ Test addition. [with a=4, b=5, expected=9] ... ok
+ 0: test_subtraction with param(*(5, 4, 1)) ... ok
+ 1: test_subtraction with param(*(9, 6, 3)) ... ok
+
+ ----------------------------------------------------------------------
+ Ran 4 tests in 0.001s
+
+ OK
+
+Finally ``@parameterized_class`` parameterizes an entire class, using
+either a list of attributes, or a list of dicts that will be applied to the
+class:
+
+.. code:: python
+
+ from yourapp.models import User
+ from parameterized import parameterized_class
+
+ @parameterized_class([
+ { "username": "user_1", "access_level": 1 },
+ { "username": "user_2", "access_level": 2, "expected_status_code": 404 },
+ ])
+ class TestUserAccessLevel(TestCase):
+ expected_status_code = 200
+
+ def setUp(self):
+ self.client.force_login(User.objects.get(username=self.username)[0])
+
+ def test_url_a(self):
+ response = self.client.get('/url')
+ self.assertEqual(response.status_code, self.expected_status_code)
+
+ def tearDown(self):
+ self.client.logout()
+
+
+ @parameterized_class(("username", "access_level", "expected_status_code"), [
+ ("user_1", 1, 200),
+ ("user_2", 2, 404)
+ ])
+ class TestUserAccessLevel(TestCase):
+ def setUp(self):
+ self.client.force_login(User.objects.get(username=self.username)[0])
+
+ def test_url_a(self):
+ response = self.client.get("/url")
+ self.assertEqual(response.status_code, self.expected_status_code)
+
+ def tearDown(self):
+ self.client.logout()
+
+
+The ``@parameterized_class`` decorator accepts a ``class_name_func`` argument,
+which controls the name of the parameterized classes generated by
+``@parameterized_class``:
+
+.. code:: python
+
+ from parameterized import parameterized, parameterized_class
+
+ def get_class_name(cls, num, params_dict):
+ # By default the generated class named includes either the "name"
+ # parameter (if present), or the first string value. This example shows
+ # multiple parameters being included in the generated class name:
+ return "%s_%s_%s%s" %(
+ cls.__name__,
+ num,
+ parameterized.to_safe_name(params_dict['a']),
+ parameterized.to_safe_name(params_dict['b']),
+ )
+
+ @parameterized_class([
+ { "a": "hello", "b": " world!", "expected": "hello world!" },
+ { "a": "say ", "b": " cheese :)", "expected": "say cheese :)" },
+ ], class_name_func=get_class_name)
+ class TestConcatenation(TestCase):
+ def test_concat(self):
+ self.assertEqual(self.a + self.b, self.expected)
+
+::
+
+ $ nosetests -v test_math.py
+ test_concat (test_concat.TestConcatenation_0_hello_world_) ... ok
+ test_concat (test_concat.TestConcatenation_0_say_cheese__) ... ok
+
+
+
+Using with Single Parameters
+............................
+
+If a test function only accepts one parameter and the value is not iterable,
+then it is possible to supply a list of values without wrapping each one in a
+tuple:
+
+.. code:: python
+
+ @parameterized([1, 2, 3])
+ def test_greater_than_zero(value):
+ assert value > 0
+
+Note, however, that if the single parameter *is* iterable (such as a list or
+tuple), then it *must* be wrapped in a tuple, list, or the ``param(...)``
+helper:
+
+.. code:: python
+
+ @parameterized([
+ ([1, 2, 3], ),
+ ([3, 3], ),
+ ([6], ),
+ ])
+ def test_sums_to_6(numbers):
+ assert sum(numbers) == 6
+
+(note, also, that Python requires single element tuples to be defined with a
+trailing comma: ``(foo, )``)
+
+
+Using with ``@mock.patch``
+..........................
+
+``parameterized`` can be used with ``mock.patch``, but the argument ordering
+can be confusing. The ``@mock.patch(...)`` decorator must come *below* the
+``@parameterized(...)``, and the mocked parameters must come *last*:
+
+.. code:: python
+
+ @mock.patch("os.getpid")
+ class TestOS(object):
+ @parameterized(...)
+ @mock.patch("os.fdopen")
+ @mock.patch("os.umask")
+ def test_method(self, param1, param2, ..., mock_umask, mock_fdopen, mock_getpid):
+ ...
+
+Note: the same holds true when using ``@parameterized.expand``.
+
+
+Migrating from ``nose-parameterized`` to ``parameterized``
+----------------------------------------------------------
+
+To migrate a codebase from ``nose-parameterized`` to ``parameterized``:
+
+1. Update your requirements file, replacing ``nose-parameterized`` with
+ ``parameterized``.
+
+2. Replace all references to ``nose_parameterized`` with ``parameterized``::
+
+ $ perl -pi -e 's/nose_parameterized/parameterized/g' your-codebase/
+
+3. You're done!
+
+
+FAQ
+---
+
+What happened to ``nose-parameterized``?
+ Originally only nose was supported. But now everything is supported, and it
+ only made sense to change the name!
+
+What do you mean when you say "nose is best supported"?
+ There are small caveates with ``py.test`` and ``unittest``: ``py.test``
+ does not show the parameter values (ex, it will show ``test_add[0]``
+ instead of ``test_add[1, 2, 3]``), and ``unittest``/``unittest2`` do not
+ support test generators so ``@parameterized.expand`` must be used.
+
+Why not use ``@pytest.mark.parametrize``?
+ Because spelling is difficult. Also, ``parameterized`` doesn't require you
+ to repeat argument names, and (using ``param``) it supports optional
+ keyword arguments.
+
+Why do I get an ``AttributeError: 'function' object has no attribute 'expand'`` with ``@parameterized.expand``?
+ You've likely installed the ``parametrized`` (note the missing *e*)
+ package. Use ``parameterized`` (with the *e*) instead and you'll be all
+ set.