parameterized-0.7.0/0000755000076600001200000000000013426453746015161 5ustar woleveradmin00000000000000parameterized-0.7.0/PKG-INFO0000644000076600001200000004604213426453746016264 0ustar woleveradmin00000000000000Metadata-Version: 1.1 Name: parameterized Version: 0.7.0 Summary: Parameterized testing with any Python test framework Home-page: https://github.com/wolever/parameterized Author: David Wolever Author-email: david@wolever.net License: FreeBSD Description: Parameterized testing with any Python test framework ==================================================== .. image:: https://travis-ci.org/wolever/parameterized.svg?branch=master :target: https://travis-ci.org/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`__. __ 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 - PyPy * - nose - yes - yes - yes - yes - yes - yes - yes * - nose2 - yes - yes - yes - yes - yes - yes - yes * - py.test - yes - yes - yes - yes - yes - yes - yes * - | unittest | (``@parameterized.expand``) - yes - yes - yes - yes - yes - yes - yes * - | unittest2 | (``@parameterized.expand``) - yes - yes - yes - yes - yes - yes - yes 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 ``testcase_func_name`` 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), ], testcase_func_name=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.expand_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", "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() @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() 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! 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*:: @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``. 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. Platform: UNKNOWN Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 3 Classifier: License :: OSI Approved :: BSD License parameterized-0.7.0/parameterized.egg-info/0000755000076600001200000000000013426453746021507 5ustar woleveradmin00000000000000parameterized-0.7.0/parameterized.egg-info/PKG-INFO0000644000076600001200000004604213426453746022612 0ustar woleveradmin00000000000000Metadata-Version: 1.1 Name: parameterized Version: 0.7.0 Summary: Parameterized testing with any Python test framework Home-page: https://github.com/wolever/parameterized Author: David Wolever Author-email: david@wolever.net License: FreeBSD Description: Parameterized testing with any Python test framework ==================================================== .. image:: https://travis-ci.org/wolever/parameterized.svg?branch=master :target: https://travis-ci.org/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`__. __ 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 - PyPy * - nose - yes - yes - yes - yes - yes - yes - yes * - nose2 - yes - yes - yes - yes - yes - yes - yes * - py.test - yes - yes - yes - yes - yes - yes - yes * - | unittest | (``@parameterized.expand``) - yes - yes - yes - yes - yes - yes - yes * - | unittest2 | (``@parameterized.expand``) - yes - yes - yes - yes - yes - yes - yes 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 ``testcase_func_name`` 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), ], testcase_func_name=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.expand_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", "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() @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() 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! 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*:: @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``. 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. Platform: UNKNOWN Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 3 Classifier: License :: OSI Approved :: BSD License parameterized-0.7.0/parameterized.egg-info/SOURCES.txt0000644000076600001200000000067713426453746023405 0ustar woleveradmin00000000000000.gitignore .hgignore .travis.yml CHANGELOG.txt LICENSE.txt README.rst setup.cfg setup.py tox.ini misspelling-helper/nose-parametrized/MANIFEST misspelling-helper/nose-parametrized/setup.py parameterized/__init__.py parameterized/parameterized.py parameterized/test.py parameterized.egg-info/PKG-INFO parameterized.egg-info/SOURCES.txt parameterized.egg-info/dependency_links.txt parameterized.egg-info/pbr.json parameterized.egg-info/top_level.txtparameterized-0.7.0/parameterized.egg-info/pbr.json0000644000076600001200000000005713426453746023167 0ustar woleveradmin00000000000000{"is_release": false, "git_version": "ec636fe"}parameterized-0.7.0/parameterized.egg-info/top_level.txt0000644000076600001200000000001613426453746024236 0ustar woleveradmin00000000000000parameterized parameterized-0.7.0/parameterized.egg-info/dependency_links.txt0000644000076600001200000000000113426453746025555 0ustar woleveradmin00000000000000 parameterized-0.7.0/parameterized/0000755000076600001200000000000013426453746020015 5ustar woleveradmin00000000000000parameterized-0.7.0/parameterized/__init__.py0000644000076600001200000000010513426453346022116 0ustar woleveradmin00000000000000from .parameterized import parameterized, param, parameterized_class parameterized-0.7.0/parameterized/test.py0000644000076600001200000003754513426453056021356 0ustar woleveradmin00000000000000# coding=utf-8 import inspect import mock from unittest import TestCase from nose.tools import assert_equal, assert_raises from .parameterized import ( PY3, PY2, parameterized, param, parameterized_argument_value_pairs, short_repr, detect_runner, parameterized_class, SkipTest, ) def assert_contains(haystack, needle): if needle not in haystack: raise AssertionError("%r not in %r" %(needle, haystack)) runner = detect_runner() UNITTEST = runner.startswith("unittest") NOSE2 = (runner == "nose2") PYTEST = (runner == "pytest") SKIP_FLAGS = { "generator": UNITTEST, # nose2 doesn't run tests on old-style classes under Py2, so don't expect # these tests to run under nose2. "py2nose2": (PY2 and NOSE2), "pytest": PYTEST, } missing_tests = set() def expect(skip, tests=None): if tests is None: tests = skip skip = None if any(SKIP_FLAGS.get(f) for f in (skip or "").split()): return missing_tests.update(tests) test_params = [ (42, ), "foo0", param("foo1"), param("foo2", bar=42), ] expect("generator", [ "test_naked_function('foo0', bar=None)", "test_naked_function('foo1', bar=None)", "test_naked_function('foo2', bar=42)", "test_naked_function(42, bar=None)", ]) @parameterized(test_params) def test_naked_function(foo, bar=None): missing_tests.remove("test_naked_function(%r, bar=%r)" %(foo, bar)) class TestParameterized(object): expect("generator", [ "test_instance_method('foo0', bar=None)", "test_instance_method('foo1', bar=None)", "test_instance_method('foo2', bar=42)", "test_instance_method(42, bar=None)", ]) @parameterized(test_params) def test_instance_method(self, foo, bar=None): missing_tests.remove("test_instance_method(%r, bar=%r)" %(foo, bar)) if not PYTEST: # py.test doesn't use xunit-style setup/teardown, so these tests don't apply class TestSetupTeardown(object): expect("generator", [ "test_setup(setup 1)", "teardown_called(teardown 1)", "test_setup(setup 2)", "teardown_called(teardown 2)", ]) stack = ["setup 1", "teardown 1", "setup 2", "teardown 2"] actual_order = "error: setup not called" def setUp(self): self.actual_order = self.stack.pop(0) def tearDown(self): missing_tests.remove("teardown_called(%s)" %(self.stack.pop(0), )) @parameterized([(1, ), (2, )]) def test_setup(self, count, *a): assert_equal(self.actual_order, "setup %s" %(count, )) missing_tests.remove("test_setup(%s)" %(self.actual_order, )) def custom_naming_func(custom_tag): def custom_naming_func(testcase_func, param_num, param): return testcase_func.__name__ + ('_%s_name_' % custom_tag) + str(param.args[0]) return custom_naming_func @mock.patch("os.getpid") class TestParameterizedExpandWithMockPatchForClass(TestCase): expect([ "test_one_function_patch_decorator('foo1', 'umask', 'getpid')", "test_one_function_patch_decorator('foo0', 'umask', 'getpid')", "test_one_function_patch_decorator(42, 'umask', 'getpid')", ]) @parameterized.expand([(42, ), "foo0", param("foo1")]) @mock.patch("os.umask") def test_one_function_patch_decorator(self, foo, mock_umask, mock_getpid): missing_tests.remove("test_one_function_patch_decorator(%r, %r, %r)" % (foo, mock_umask._mock_name, mock_getpid._mock_name)) expect([ "test_multiple_function_patch_decorator" "(42, 51, 'umask', 'fdopen', 'getpid')", "test_multiple_function_patch_decorator" "('foo0', 'bar0', 'umask', 'fdopen', 'getpid')", "test_multiple_function_patch_decorator" "('foo1', 'bar1', 'umask', 'fdopen', 'getpid')", ]) @parameterized.expand([(42, 51), ("foo0", "bar0"), param("foo1", "bar1")]) @mock.patch("os.fdopen") @mock.patch("os.umask") def test_multiple_function_patch_decorator(self, foo, bar, mock_umask, mock_fdopen, mock_getpid): missing_tests.remove("test_multiple_function_patch_decorator" "(%r, %r, %r, %r, %r)" % (foo, bar, mock_umask._mock_name, mock_fdopen._mock_name, mock_getpid._mock_name)) @mock.patch("os.getpid") class TestParameterizedExpandWithNoExpand(object): expect("generator", [ "test_patch_class_no_expand(42, 51, 'umask', 'getpid')", ]) @parameterized([(42, 51)]) @mock.patch("os.umask") def test_patch_class_no_expand(self, foo, bar, mock_umask, mock_getpid): missing_tests.remove("test_patch_class_no_expand" "(%r, %r, %r, %r)" % (foo, bar, mock_umask._mock_name, mock_getpid._mock_name)) class TestParameterizedExpandWithNoMockPatchForClass(TestCase): expect([ "test_one_function_patch_decorator('foo1', 'umask')", "test_one_function_patch_decorator('foo0', 'umask')", "test_one_function_patch_decorator(42, 'umask')", ]) @parameterized.expand([(42, ), "foo0", param("foo1")]) @mock.patch("os.umask") def test_one_function_patch_decorator(self, foo, mock_umask): missing_tests.remove("test_one_function_patch_decorator(%r, %r)" % (foo, mock_umask._mock_name)) expect([ "test_multiple_function_patch_decorator" "(42, 51, 'umask', 'fdopen')", "test_multiple_function_patch_decorator" "('foo0', 'bar0', 'umask', 'fdopen')", "test_multiple_function_patch_decorator" "('foo1', 'bar1', 'umask', 'fdopen')", ]) @parameterized.expand([(42, 51), ("foo0", "bar0"), param("foo1", "bar1")]) @mock.patch("os.fdopen") @mock.patch("os.umask") def test_multiple_function_patch_decorator(self, foo, bar, mock_umask, mock_fdopen): missing_tests.remove("test_multiple_function_patch_decorator" "(%r, %r, %r, %r)" % (foo, bar, mock_umask._mock_name, mock_fdopen._mock_name)) class TestParameterizedExpandWithNoMockPatchForClassNoExpand(object): expect("generator", [ "test_patch_no_expand(42, 51, 'umask')", ]) @parameterized([(42, 51)]) @mock.patch("os.umask") def test_patch_no_expand(self, foo, bar, mock_umask): missing_tests.remove("test_patch_no_expand(%r, %r, %r)" % (foo, bar, mock_umask._mock_name)) class TestParamerizedOnTestCase(TestCase): expect([ "test_on_TestCase('foo0', bar=None)", "test_on_TestCase('foo1', bar=None)", "test_on_TestCase('foo2', bar=42)", "test_on_TestCase(42, bar=None)", ]) @parameterized.expand(test_params) def test_on_TestCase(self, foo, bar=None): missing_tests.remove("test_on_TestCase(%r, bar=%r)" %(foo, bar)) expect([ "test_on_TestCase2_custom_name_42(42, bar=None)", "test_on_TestCase2_custom_name_foo0('foo0', bar=None)", "test_on_TestCase2_custom_name_foo1('foo1', bar=None)", "test_on_TestCase2_custom_name_foo2('foo2', bar=42)", ]) @parameterized.expand(test_params, name_func=custom_naming_func("custom")) def test_on_TestCase2(self, foo, bar=None): stack = inspect.stack() frame = stack[1] frame_locals = frame[0].f_locals nose_test_method_name = frame_locals['a'][0]._testMethodName expected_name = "test_on_TestCase2_custom_name_" + str(foo) assert_equal(nose_test_method_name, expected_name, "Test Method name '%s' did not get customized to expected: '%s'" % (nose_test_method_name, expected_name)) missing_tests.remove("%s(%r, bar=%r)" %(expected_name, foo, bar)) class TestParameterizedExpandDocstring(TestCase): def _assert_docstring(self, expected_docstring, rstrip=False): """ Checks the current test method's docstring. Must be called directly from the test method. """ stack = inspect.stack() f_locals = stack[3][0].f_locals test_method = ( f_locals.get("testMethod") or # Py27 f_locals.get("function") # Py33 ) if test_method is None: raise AssertionError("uh oh, unittest changed a local variable name") actual_docstring = test_method.__doc__ if rstrip: actual_docstring = actual_docstring.rstrip() assert_equal(actual_docstring, expected_docstring) @parameterized.expand([param("foo")], doc_func=lambda f, n, p: "stuff") def test_custom_doc_func(self, foo, bar=None): """Documentation""" self._assert_docstring("stuff") @parameterized.expand([param("foo")]) def test_single_line_docstring(self, foo): """Documentation.""" self._assert_docstring("Documentation [with foo=%r]." %(foo, )) @parameterized.expand([param("foo")]) def test_empty_docstring(self, foo): "" self._assert_docstring("[with foo=%r]" %(foo, )) @parameterized.expand([param("foo")]) def test_multiline_documentation(self, foo): """Documentation. More""" self._assert_docstring( "Documentation [with foo=%r].\n\n" " More" %(foo, ) ) @parameterized.expand([param("foo")]) def test_unicode_docstring(self, foo): u"""Döcumentation.""" self._assert_docstring(u"Döcumentation [with foo=%r]." %(foo, )) @parameterized.expand([param("foo", )]) def test_default_values_get_correct_value(self, foo, bar=12): """Documentation""" self._assert_docstring("Documentation [with foo=%r, bar=%r]" %(foo, bar)) @parameterized.expand([param("foo", )]) def test_with_leading_newline(self, foo, bar=12): """ Documentation """ self._assert_docstring("Documentation [with foo=%r, bar=%r]" %(foo, bar), rstrip=True) def test_warns_when_using_parameterized_with_TestCase(): try: class TestTestCaseWarnsOnBadUseOfParameterized(TestCase): @parameterized([(42, )]) def test_in_subclass_of_TestCase(self, foo): pass except Exception as e: assert_contains(str(e), "parameterized.expand") else: raise AssertionError("Expected exception not raised") def test_helpful_error_on_invalid_parameters(): try: parameterized([1432141234243])(lambda: None) except Exception as e: assert_contains(str(e), "Parameters must be tuples") else: raise AssertionError("Expected exception not raised") def test_helpful_error_on_empty_iterable_input(): try: parameterized([])(lambda: None) except ValueError as e: assert_contains(str(e), "iterable is empty") else: raise AssertionError("Expected exception not raised") def test_skip_test_on_empty_iterable(): func = parameterized([], skip_on_empty=True)(lambda: None) assert_raises(SkipTest, func) def test_helpful_error_on_empty_iterable_input_expand(): try: class ExpectErrorOnEmptyInput(TestCase): @parameterized.expand([]) def test_expect_error(self): pass except ValueError as e: assert_contains(str(e), "iterable is empty") else: raise AssertionError("Expected exception not raised") expect("generator", [ "test_wrapped_iterable_input('foo')", ]) @parameterized(lambda: iter(["foo"])) def test_wrapped_iterable_input(foo): missing_tests.remove("test_wrapped_iterable_input(%r)" %(foo, )) def test_helpful_error_on_non_iterable_input(): try: parameterized(lambda: 42)(lambda: None) except Exception as e: assert_contains(str(e), "is not iterable") else: raise AssertionError("Expected exception not raised") def tearDownModule(): missing = sorted(list(missing_tests)) assert_equal(missing, []) def test_old_style_classes(): if PY3: raise SkipTest("Py3 doesn't have old-style classes") class OldStyleClass: @parameterized(["foo"]) def parameterized_method(self, param): pass try: list(OldStyleClass().parameterized_method()) except TypeError as e: assert_contains(str(e), "new-style") assert_contains(str(e), "parameterized.expand") assert_contains(str(e), "OldStyleClass") else: raise AssertionError("expected TypeError not raised by old-style class") class TestOldStyleClass: expect("py2nose2 generator", [ "test_on_old_style_class('foo')", "test_on_old_style_class('bar')", ]) @parameterized.expand(["foo", "bar"]) def test_old_style_classes(self, param): missing_tests.remove("test_on_old_style_class(%r)" %(param, )) @parameterized([ ("", param(), []), ("*a, **kw", param(), []), ("*a, **kw", param(1, foo=42), [("*a", (1, )), ("**kw", {"foo": 42})]), ("foo", param(1), [("foo", 1)]), ("foo, *a", param(1), [("foo", 1)]), ("foo, *a", param(1, 9), [("foo", 1), ("*a", (9, ))]), ("foo, *a, **kw", param(1, bar=9), [("foo", 1), ("**kw", {"bar": 9})]), ("x=9", param(), [("x", 9)]), ("x=9", param(1), [("x", 1)]), ("x, y=9, *a, **kw", param(1), [("x", 1), ("y", 9)]), ("x, y=9, *a, **kw", param(1, 2), [("x", 1), ("y", 2)]), ("x, y=9, *a, **kw", param(1, 2, 3), [("x", 1), ("y", 2), ("*a", (3, ))]), ("x, y=9, *a, **kw", param(1, y=2), [("x", 1), ("y", 2)]), ("x, y=9, *a, **kw", param(1, z=2), [("x", 1), ("y", 9), ("**kw", {"z": 2})]), ("x, y=9, *a, **kw", param(1, 2, 3, z=3), [("x", 1), ("y", 2), ("*a", (3, )), ("**kw", {"z": 3})]), ]) def test_parameterized_argument_value_pairs(func_params, p, expected): helper = eval("lambda %s: None" %(func_params, )) actual = parameterized_argument_value_pairs(helper, p) assert_equal(actual, expected) @parameterized([ ("abcd", "'abcd'"), ("123456789", "'12...89'"), (123456789, "123...789"), (123456789, "12...89", 4), ]) def test_short_repr(input, expected, n=6): assert_equal(short_repr(input, n=n), expected) @parameterized([ ("foo", ), ]) def test_with_docstring(input): """ Docstring! """ pass cases_over_10 = [(i, i+1) for i in range(11)] @parameterized(cases_over_10) def test_cases_over_10(input, expected): assert_equal(input, expected-1) @parameterized_class(("a", "b", "c"), [ ("foo", 1, 2), ("bar", 3, 0), (0, 1, 2), ]) class TestParameterizedClass(TestCase): expect([ "TestParameterizedClass_0_foo:test_method_a('foo', 1, 2)", "TestParameterizedClass_0_foo:test_method_b('foo', 1, 2)", "TestParameterizedClass_1_bar:test_method_a('bar', 3, 0)", "TestParameterizedClass_1_bar:test_method_b('bar', 3, 0)", "TestParameterizedClass_2:test_method_a(0, 1, 2)", "TestParameterizedClass_2:test_method_b(0, 1, 2)", ]) def _assertions(self, test_name): assert hasattr(self, "a") assert_equal(self.b + self.c, 3) missing_tests.remove("%s:%s(%r, %r, %r)" %( self.__class__.__name__, test_name, self.a, self.b, self.c, )) def test_method_a(self): self._assertions("test_method_a") def test_method_b(self): self._assertions("test_method_b") @parameterized_class([ {"foo": 1}, {"bar": 1}, ]) class TestParameterizedClassDict(TestCase): expect([ "TestParameterizedClassDict_0:test_method(1, 0)", "TestParameterizedClassDict_1:test_method(0, 1)", ]) foo = 0 bar = 0 def test_method(self): missing_tests.remove("%s:test_method(%r, %r)" %( self.__class__.__name__, self.foo, self.bar, )) parameterized-0.7.0/parameterized/parameterized.py0000644000076600001200000005140713426450777023233 0ustar woleveradmin00000000000000import re import sys import inspect import warnings from functools import wraps from types import MethodType as MethodType from collections import namedtuple try: from collections import OrderedDict as MaybeOrderedDict except ImportError: MaybeOrderedDict = dict from unittest import TestCase try: from unittest import SkipTest except ImportError: class SkipTest(Exception): pass PY3 = sys.version_info[0] == 3 PY2 = sys.version_info[0] == 2 if PY3: # Python 3 doesn't have an InstanceType, so just use a dummy type. class InstanceType(): pass lzip = lambda *a: list(zip(*a)) text_type = str string_types = str, bytes_type = bytes def make_method(func, instance, type): if instance is None: return func return MethodType(func, instance) else: from types import InstanceType lzip = zip text_type = unicode bytes_type = str string_types = basestring, def make_method(func, instance, type): return MethodType(func, instance, type) CompatArgSpec = namedtuple("CompatArgSpec", "args varargs keywords defaults") def getargspec(func): if PY2: return CompatArgSpec(*inspect.getargspec(func)) args = inspect.getfullargspec(func) if args.kwonlyargs: raise TypeError(( "parameterized does not (yet) support functions with keyword " "only arguments, but %r has keyword only arguments. " "Please open an issue with your usecase if this affects you: " "https://github.com/wolever/parameterized/issues/new" ) %(func, )) return CompatArgSpec(*args[:4]) _param = namedtuple("param", "args kwargs") def skip_on_empty_helper(*a, **kw): raise SkipTest("parameterized input is empty") def reapply_patches_if_need(func): def dummy_wrapper(orgfunc): @wraps(orgfunc) def dummy_func(*args, **kwargs): return orgfunc(*args, **kwargs) return dummy_func if hasattr(func, 'patchings'): func = dummy_wrapper(func) tmp_patchings = func.patchings delattr(func, 'patchings') for patch_obj in tmp_patchings: func = patch_obj.decorate_callable(func) return func def delete_patches_if_need(func): if hasattr(func, 'patchings'): func.patchings[:] = [] class param(_param): """ Represents a single parameter to a test case. For example:: >>> p = param("foo", bar=16) >>> p param("foo", bar=16) >>> p.args ('foo', ) >>> p.kwargs {'bar': 16} Intended to be used as an argument to ``@parameterized``:: @parameterized([ param("foo", bar=16), ]) def test_stuff(foo, bar=16): pass """ def __new__(cls, *args , **kwargs): return _param.__new__(cls, args, kwargs) @classmethod def explicit(cls, args=None, kwargs=None): """ Creates a ``param`` by explicitly specifying ``args`` and ``kwargs``:: >>> param.explicit([1,2,3]) param(*(1, 2, 3)) >>> param.explicit(kwargs={"foo": 42}) param(*(), **{"foo": "42"}) """ args = args or () kwargs = kwargs or {} return cls(*args, **kwargs) @classmethod def from_decorator(cls, args): """ Returns an instance of ``param()`` for ``@parameterized`` argument ``args``:: >>> param.from_decorator((42, )) param(args=(42, ), kwargs={}) >>> param.from_decorator("foo") param(args=("foo", ), kwargs={}) """ if isinstance(args, param): return args elif isinstance(args, string_types): args = (args, ) try: return cls(*args) except TypeError as e: if "after * must be" not in str(e): raise raise TypeError( "Parameters must be tuples, but %r is not (hint: use '(%r, )')" %(args, args), ) def __repr__(self): return "param(*%r, **%r)" %self class QuietOrderedDict(MaybeOrderedDict): """ When OrderedDict is available, use it to make sure that the kwargs in doc strings are consistently ordered. """ __str__ = dict.__str__ __repr__ = dict.__repr__ def parameterized_argument_value_pairs(func, p): """Return tuples of parameterized arguments and their values. This is useful if you are writing your own doc_func function and need to know the values for each parameter name:: >>> def func(a, foo=None, bar=42, **kwargs): pass >>> p = param(1, foo=7, extra=99) >>> parameterized_argument_value_pairs(func, p) [("a", 1), ("foo", 7), ("bar", 42), ("**kwargs", {"extra": 99})] If the function's first argument is named ``self`` then it will be ignored:: >>> def func(self, a): pass >>> p = param(1) >>> parameterized_argument_value_pairs(func, p) [("a", 1)] Additionally, empty ``*args`` or ``**kwargs`` will be ignored:: >>> def func(foo, *args): pass >>> p = param(1) >>> parameterized_argument_value_pairs(func, p) [("foo", 1)] >>> p = param(1, 16) >>> parameterized_argument_value_pairs(func, p) [("foo", 1), ("*args", (16, ))] """ argspec = getargspec(func) arg_offset = 1 if argspec.args[:1] == ["self"] else 0 named_args = argspec.args[arg_offset:] result = lzip(named_args, p.args) named_args = argspec.args[len(result) + arg_offset:] varargs = p.args[len(result):] result.extend([ (name, p.kwargs.get(name, default)) for (name, default) in zip(named_args, argspec.defaults or []) ]) seen_arg_names = set([ n for (n, _) in result ]) keywords = QuietOrderedDict(sorted([ (name, p.kwargs[name]) for name in p.kwargs if name not in seen_arg_names ])) if varargs: result.append(("*%s" %(argspec.varargs, ), tuple(varargs))) if keywords: result.append(("**%s" %(argspec.keywords, ), keywords)) return result def short_repr(x, n=64): """ A shortened repr of ``x`` which is guaranteed to be ``unicode``:: >>> short_repr("foo") u"foo" >>> short_repr("123456789", n=4) u"12...89" """ x_repr = repr(x) if isinstance(x_repr, bytes_type): try: x_repr = text_type(x_repr, "utf-8") except UnicodeDecodeError: x_repr = text_type(x_repr, "latin1") if len(x_repr) > n: x_repr = x_repr[:n//2] + "..." + x_repr[len(x_repr) - n//2:] return x_repr def default_doc_func(func, num, p): if func.__doc__ is None: return None all_args_with_values = parameterized_argument_value_pairs(func, p) # Assumes that the function passed is a bound method. descs = ["%s=%s" %(n, short_repr(v)) for n, v in all_args_with_values] # The documentation might be a multiline string, so split it # and just work with the first string, ignoring the period # at the end if there is one. first, nl, rest = func.__doc__.lstrip().partition("\n") suffix = "" if first.endswith("."): suffix = "." first = first[:-1] args = "%s[with %s]" %(len(first) and " " or "", ", ".join(descs)) return "".join([first.rstrip(), args, suffix, nl, rest]) def default_name_func(func, num, p): base_name = func.__name__ name_suffix = "_%s" %(num, ) if len(p.args) > 0 and isinstance(p.args[0], string_types): name_suffix += "_" + parameterized.to_safe_name(p.args[0]) return base_name + name_suffix _test_runner_override = None _test_runner_guess = False _test_runners = set(["unittest", "unittest2", "nose", "nose2", "pytest"]) _test_runner_aliases = { "_pytest": "pytest", } def set_test_runner(name): global _test_runner_override if name not in _test_runners: raise TypeError( "Invalid test runner: %r (must be one of: %s)" %(name, ", ".join(_test_runners)), ) _test_runner_override = name def detect_runner(): """ Guess which test runner we're using by traversing the stack and looking for the first matching module. This *should* be reasonably safe, as it's done during test disocvery where the test runner should be the stack frame immediately outside. """ if _test_runner_override is not None: return _test_runner_override global _test_runner_guess if _test_runner_guess is False: stack = inspect.stack() for record in reversed(stack): frame = record[0] module = frame.f_globals.get("__name__").partition(".")[0] if module in _test_runner_aliases: module = _test_runner_aliases[module] if module in _test_runners: _test_runner_guess = module break if record[1].endswith("python2.6/unittest.py"): _test_runner_guess = "unittest" break else: _test_runner_guess = None return _test_runner_guess class parameterized(object): """ Parameterize a test case:: class TestInt(object): @parameterized([ ("A", 10), ("F", 15), param("10", 42, base=42) ]) def test_int(self, input, expected, base=16): actual = int(input, base=base) assert_equal(actual, expected) @parameterized([ (2, 3, 5) (3, 5, 8), ]) def test_add(a, b, expected): assert_equal(a + b, expected) """ def __init__(self, input, doc_func=None, skip_on_empty=False): self.get_input = self.input_as_callable(input) self.doc_func = doc_func or default_doc_func self.skip_on_empty = skip_on_empty def __call__(self, test_func): self.assert_not_in_testcase_subclass() @wraps(test_func) def wrapper(test_self=None): test_cls = test_self and type(test_self) if test_self is not None: if issubclass(test_cls, InstanceType): raise TypeError(( "@parameterized can't be used with old-style classes, but " "%r has an old-style class. Consider using a new-style " "class, or '@parameterized.expand' " "(see http://stackoverflow.com/q/54867/71522 for more " "information on old-style classes)." ) %(test_self, )) original_doc = wrapper.__doc__ for num, args in enumerate(wrapper.parameterized_input): p = param.from_decorator(args) unbound_func, nose_tuple = self.param_as_nose_tuple(test_self, test_func, num, p) try: wrapper.__doc__ = nose_tuple[0].__doc__ # Nose uses `getattr(instance, test_func.__name__)` to get # a method bound to the test instance (as opposed to a # method bound to the instance of the class created when # tests were being enumerated). Set a value here to make # sure nose can get the correct test method. if test_self is not None: setattr(test_cls, test_func.__name__, unbound_func) yield nose_tuple finally: if test_self is not None: delattr(test_cls, test_func.__name__) wrapper.__doc__ = original_doc input = self.get_input() if not input: if not self.skip_on_empty: raise ValueError( "Parameters iterable is empty (hint: use " "`parameterized([], skip_on_empty=True)` to skip " "this test when the input is empty)" ) wrapper = wraps(test_func)(lambda: skip_on_empty_helper()) wrapper.parameterized_input = input wrapper.parameterized_func = test_func test_func.__name__ = "_parameterized_original_%s" %(test_func.__name__, ) return wrapper def param_as_nose_tuple(self, test_self, func, num, p): nose_func = wraps(func)(lambda *args: func(*args[:-1], **args[-1])) nose_func.__doc__ = self.doc_func(func, num, p) # Track the unbound function because we need to setattr the unbound # function onto the class for nose to work (see comments above), and # Python 3 doesn't let us pull the function out of a bound method. unbound_func = nose_func if test_self is not None: # Under nose on Py2 we need to return an unbound method to make # sure that the `self` in the method is properly shared with the # `self` used in `setUp` and `tearDown`. But only there. Everyone # else needs a bound method. func_self = ( None if PY2 and detect_runner() == "nose" else test_self ) nose_func = make_method(nose_func, func_self, type(test_self)) return unbound_func, (nose_func, ) + p.args + (p.kwargs or {}, ) def assert_not_in_testcase_subclass(self): parent_classes = self._terrible_magic_get_defining_classes() if any(issubclass(cls, TestCase) for cls in parent_classes): raise Exception("Warning: '@parameterized' tests won't work " "inside subclasses of 'TestCase' - use " "'@parameterized.expand' instead.") def _terrible_magic_get_defining_classes(self): """ Returns the set of parent classes of the class currently being defined. Will likely only work if called from the ``parameterized`` decorator. This function is entirely @brandon_rhodes's fault, as he suggested the implementation: http://stackoverflow.com/a/8793684/71522 """ stack = inspect.stack() if len(stack) <= 4: return [] frame = stack[4] code_context = frame[4] and frame[4][0].strip() if not (code_context and code_context.startswith("class ")): return [] _, _, parents = code_context.partition("(") parents, _, _ = parents.partition(")") return eval("[" + parents + "]", frame[0].f_globals, frame[0].f_locals) @classmethod def input_as_callable(cls, input): if callable(input): return lambda: cls.check_input_values(input()) input_values = cls.check_input_values(input) return lambda: input_values @classmethod def check_input_values(cls, input_values): # Explicitly convery non-list inputs to a list so that: # 1. A helpful exception will be raised if they aren't iterable, and # 2. Generators are unwrapped exactly once (otherwise `nosetests # --processes=n` has issues; see: # https://github.com/wolever/nose-parameterized/pull/31) if not isinstance(input_values, list): input_values = list(input_values) return [ param.from_decorator(p) for p in input_values ] @classmethod def expand(cls, input, name_func=None, doc_func=None, skip_on_empty=False, **legacy): """ A "brute force" method of parameterizing test cases. Creates new test cases and injects them into the namespace that the wrapped function is being defined in. Useful for parameterizing tests in subclasses of 'UnitTest', where Nose test generators don't work. >>> @parameterized.expand([("foo", 1, 2)]) ... def test_add1(name, input, expected): ... actual = add1(input) ... assert_equal(actual, expected) ... >>> locals() ... 'test_add1_foo_0': ... >>> """ if "testcase_func_name" in legacy: warnings.warn("testcase_func_name= is deprecated; use name_func=", DeprecationWarning, stacklevel=2) if not name_func: name_func = legacy["testcase_func_name"] if "testcase_func_doc" in legacy: warnings.warn("testcase_func_doc= is deprecated; use doc_func=", DeprecationWarning, stacklevel=2) if not doc_func: doc_func = legacy["testcase_func_doc"] doc_func = doc_func or default_doc_func name_func = name_func or default_name_func def parameterized_expand_wrapper(f, instance=None): stack = inspect.stack() frame = stack[1] frame_locals = frame[0].f_locals paramters = cls.input_as_callable(input)() if not paramters: if not skip_on_empty: raise ValueError( "Parameters iterable is empty (hint: use " "`parameterized.expand([], skip_on_empty=True)` to skip " "this test when the input is empty)" ) return wraps(f)(lambda: skip_on_empty_helper()) digits = len(str(len(paramters) - 1)) for num, p in enumerate(paramters): name = name_func(f, "{num:0>{digits}}".format(digits=digits, num=num), p) # If the original function has patches applied by 'mock.patch', # re-construct all patches on the just former decoration layer # of param_as_standalone_func so as not to share # patch objects between new functions nf = reapply_patches_if_need(f) frame_locals[name] = cls.param_as_standalone_func(p, nf, name) frame_locals[name].__doc__ = doc_func(f, num, p) # Delete original patches to prevent new function from evaluating # original patching object as well as re-constructed patches. delete_patches_if_need(f) f.__test__ = False return parameterized_expand_wrapper @classmethod def param_as_standalone_func(cls, p, func, name): @wraps(func) def standalone_func(*a): return func(*(a + p.args), **p.kwargs) standalone_func.__name__ = name # place_as is used by py.test to determine what source file should be # used for this test. standalone_func.place_as = func # Remove __wrapped__ because py.test will try to look at __wrapped__ # to determine which parameters should be used with this test case, # and obviously we don't need it to do any parameterization. try: del standalone_func.__wrapped__ except AttributeError: pass return standalone_func @classmethod def to_safe_name(cls, s): return str(re.sub("[^a-zA-Z0-9_]+", "_", s)) def parameterized_class(attrs, input_values=None): """ Parameterizes a test class by setting attributes on the class. Can be used in two ways: 1) With a list of dictionaries containing attributes to override:: @parameterized_class([ { "username": "foo" }, { "username": "bar", "access_level": 2 }, ]) class TestUserAccessLevel(TestCase): ... 2) With a tuple of attributes, then a list of tuples of values: @parameterized_class(("username", "access_level"), [ ("foo", 1), ("bar", 2) ]) class TestUserAccessLevel(TestCase): ... """ if isinstance(attrs, string_types): attrs = [attrs] input_dicts = ( attrs if input_values is None else [dict(zip(attrs, vals)) for vals in input_values] ) def decorator(base_class): test_class_module = sys.modules[base_class.__module__].__dict__ for idx, input_dict in enumerate(input_dicts): test_class_dict = dict(base_class.__dict__) test_class_dict.update(input_dict) name_suffix = input_values and input_values[idx] if isinstance(name_suffix, (list, tuple)) and len(input_values) > 0: name_suffix = name_suffix[0] name_suffix = ( "_%s" %(name_suffix, ) if isinstance(name_suffix, string_types) else "" ) name = "%s_%s%s" %( base_class.__name__, idx, name_suffix, ) test_class_module[name] = type(name, (base_class, ), test_class_dict) return decorator parameterized-0.7.0/.hgignore0000644000076600001200000000022412075364747016763 0ustar woleveradmin00000000000000syntax: glob *.class *.o *.pyc *.sqlite3 *.sw[op] *~ .DS_Store bin-debug/* bin-release/* bin/* tags *.beam *.dump env/ *egg-info* dist/ .tox .env32 parameterized-0.7.0/setup.py0000644000076600001200000000142113426453707016666 0ustar woleveradmin00000000000000#!/usr/bin/env python import os import sys from setuptools import setup, find_packages os.chdir(os.path.dirname(sys.argv[0]) or ".") try: long_description = open("README.rst", "U").read() except IOError: long_description = "See https://github.com/wolever/parameterized" setup( name="parameterized", version="0.7.0", url="https://github.com/wolever/parameterized", license="FreeBSD", author="David Wolever", author_email="david@wolever.net", description="Parameterized testing with any Python test framework", classifiers=[ 'Programming Language :: Python', 'Programming Language :: Python :: 3', 'License :: OSI Approved :: BSD License', ], packages=find_packages(), long_description=long_description, ) parameterized-0.7.0/.gitignore0000644000076600001200000000025213412322304017124 0ustar woleveradmin00000000000000*.class *.o *.pyc *.sqlite3 *.sw[op] *~ .DS_Store bin-debug/* bin-release/* bin/* tags *.beam *.dump env/ .env/ *egg-info* misc/ dist/ Icon? .tox build/ .cache/ \.idea/ parameterized-0.7.0/tox.ini0000644000076600001200000000051713426450734016471 0ustar woleveradmin00000000000000[tox] envlist=py{27,35,36,37,py}-{nose,nose2,pytest,unit,unit2} [testenv] deps= nose mock nose2: nose2 pytest: pytest>=2,<3 unit2: unittest2 commands= nose: nosetests nose2: nose2 pytest: py.test parameterized/test.py unit: python -m unittest parameterized.test unit2: unit2 parameterized.test parameterized-0.7.0/CHANGELOG.txt0000644000076600001200000000602713426453707017213 0ustar woleveradmin000000000000000.7.0 (2019-02-05) * Added parameterized_class feature, for parameterizing entire test classes (many thanks to @TobyLL for their suggestions and help testing!) * Fix DeprecationWarning on `inspect.getargs` (thanks @brettdh; https://github.com/wolever/parameterized/issues/67) 0.6.2 (2018-03-11) * Make sure that `setUp` and `tearDown` methods work correctly (#40) * Raise a ValueError when input is empty (thanks @danielbradburn; https://github.com/wolever/parameterized/pull/48) * Fix the order when number of cases exceeds 10 (thanks @ntflc; https://github.com/wolever/parameterized/pull/49) 0.6.1 (2017-03-21) * Rename package from nose-parameterized to parameterized. A nose-parameterized package will be released with a deprecation warning. * Rename testcase_func_doc and testcase_func_name methods to doc_func and name_func (a DeprecationWarning will be issued, to be removed in 1.0). * Include parameters in all docstrings, not just `parameterized.expand` docstrings. * Explicitly unwrap iterators and generators before the test run (thanks @chornsby; https://github.com/wolever/nose-parameterized/pull/31) * 0.6.1 instead of 0.6.0 because I'm a dumb and accidentally uploaded the wrong thing to PyPI under version 0.6.0. 0.5.0 (2015-06-09) * Support for nose2, py.test, unittest, and unittest2 (nose2 support thanks to @marek-mazur; https://github.com/wolever/nose-parameterized/pull/26) 0.4.2 (2015-05-18) * Fix bug with expand + empty arguments (thanks @jikamens; https://github.com/wolever/nose-parameterized/pull/25) 0.4.1 (2015-05-17) * Fix bug with expand + empty docstring (thanks @jikamens; https://github.com/wolever/nose-parameterized/pull/24) 0.4.0 (2015-05-11) * Include parameters in ``parameterized.expand`` function docstrings (https://github.com/wolever/nose-parameterized/pull/22; thanks @smspillaz) * Drop Python 3.2 support 0.3.5 (2014-11-05) * Allow the names of test cases generated by ``parameterized.expand`` to be customized. (https://github.com/wolever/nose-parameterized/pull/19; thanks @curtissiemens) 0.3.4 (2014-10-03) * Use ``functools.wraps`` to wrap expanded functions (https://github.com/wolever/nose-parameterized/pull/17; thanks @toumorokoshi) 0.3.3 (2014-01-03) * Replace unsafe characters with "_" in names generated by ``@parameterized.expand``. 0.3.2 (2014-01-02) * Add helpful error message when used with old-style classes. 0.3.1 (2013-08-01) * Fix bug: `nose_parameterized.param` wasn't being imported. 0.3 (2013-05-18) * Add `param` class. * Add explicit support for callable inputs. * Update readme to more throughly describe useage. * Remove un-used test helpers (`setup_logging`, `teardown_logging`, `logged_messages`, `assert_logged`, `assert_no_errors_logged`, `assert_contains`, `assert_not_contains`, `assert_raises`, `imported_from_test`). 0.2 (2013-01-15) * Add Python 3 support parameterized-0.7.0/setup.cfg0000644000076600001200000000007513426453746017004 0ustar woleveradmin00000000000000[wheel] universal = 1 [egg_info] tag_build = tag_date = 0 parameterized-0.7.0/README.rst0000644000076600001200000003477213426453207016655 0ustar woleveradmin00000000000000Parameterized testing with any Python test framework ==================================================== .. image:: https://travis-ci.org/wolever/parameterized.svg?branch=master :target: https://travis-ci.org/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`__. __ 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 - PyPy * - nose - yes - yes - yes - yes - yes - yes - yes * - nose2 - yes - yes - yes - yes - yes - yes - yes * - py.test - yes - yes - yes - yes - yes - yes - yes * - | unittest | (``@parameterized.expand``) - yes - yes - yes - yes - yes - yes - yes * - | unittest2 | (``@parameterized.expand``) - yes - yes - yes - yes - yes - yes - yes 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 ``testcase_func_name`` 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), ], testcase_func_name=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.expand_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", "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() @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() 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! 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*:: @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``. 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. parameterized-0.7.0/LICENSE.txt0000644000076600001200000000313013426453562016775 0ustar woleveradmin00000000000000tl;dr: all code code is licensed under simplified BSD. Unless stated otherwise in the source files, all code is copyright 2010 David Wolever . All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. The views and conclusions contained in the software and documentation are those of the authors and should not be interpreted as representing official policies, either expressed or implied, of David Wolever. parameterized-0.7.0/.travis.yml0000644000076600001200000000212213412322301017240 0ustar woleveradmin00000000000000language: python sudo: false matrix: include: - env: "TOXENV=py27-nose" python: "2.7" - env: "TOXENV=py27-nose2" python: "2.7" - env: "TOXENV=py27-pytest" python: "2.7" - env: "TOXENV=py27-unit" python: "2.7" - env: "TOXENV=py27-unit2" python: "2.7" - env: "TOXENV=py35-nose" python: "3.5" - env: "TOXENV=py35-nose2" python: "3.5" - env: "TOXENV=py35-pytest" python: "3.5" - env: "TOXENV=py35-unit" python: "3.5" - env: "TOXENV=py35-unit2" python: "3.5" - env: "TOXENV=py36-nose" python: "3.6" - env: "TOXENV=py36-nose2" python: "3.6" - env: "TOXENV=py36-pytest" python: "3.6" - env: "TOXENV=py36-unit" python: "3.6" - env: "TOXENV=py36-unit2" python: "3.6" - env: "TOXENV=pypy-nose" python: "pypy" - env: "TOXENV=pypy-nose2" python: "pypy" - env: "TOXENV=pypy-pytest" python: "pypy" - env: "TOXENV=pypy-unit" python: "pypy" - env: "TOXENV=pypy-unit2" python: "pypy" install: pip install tox script: tox parameterized-0.7.0/misspelling-helper/0000755000076600001200000000000013426453746020764 5ustar woleveradmin00000000000000parameterized-0.7.0/misspelling-helper/nose-parametrized/0000755000076600001200000000000013426453746024415 5ustar woleveradmin00000000000000parameterized-0.7.0/misspelling-helper/nose-parametrized/MANIFEST0000644000076600001200000000006413065521622025532 0ustar woleveradmin00000000000000# file GENERATED by distutils, do NOT edit setup.py parameterized-0.7.0/misspelling-helper/nose-parametrized/setup.py0000644000076600001200000000065513065521622026121 0ustar woleveradmin00000000000000import sys from distutils.core import setup if __name__ == "__main__": if "sdist" not in sys.argv[1:]: raise ValueError("Please use the 'parameterized' (note the second 'e' in 'parameterized') pypi package instead of 'nose-parametrized'") setup( name="nose-parametrized", version="0.1", description="Please use the 'parameterized' package (note the second 'e' in 'parameterized')", )