parameterized


Nameparameterized JSON
Version 0.9.0 PyPI version JSON
download
home_page
SummaryParameterized testing with any Python test framework
upload_time2023-03-27 02:01:11
maintainer
docs_urlNone
author
requires_python>=3.7
licenseFreeBSD
keywords
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            Parameterized testing with any Python test framework
====================================================

.. image:: https://img.shields.io/pypi/v/parameterized
    :alt: PyPI
    :target: https://pypi.org/project/parameterized/

.. image:: https://img.shields.io/pypi/dm/parameterized
    :alt: PyPI - Downloads
    :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://app.circleci.com/pipelines/github/wolever/parameterized?branch=master

.. list-table::
   :header-rows: 1
   :stub-columns: 1

   * -
     - Py3.7
     - Py3.8
     - Py3.9
     - Py3.10
     - Py3.11
     - PyPy3
     - ``@mock.patch``
   * - nose
     - yes
     - yes
     - yes
     - yes
     - no§
     - no§
     - yes
   * - nose2
     - yes
     - yes
     - yes
     - yes
     - yes
     - yes
     - yes
   * - py.test 2
     - no*
     - no*
     - no*
     - no*
     - no*
     - no*
     - no*
   * - py.test 3
     - yes
     - yes
     - yes
     - yes
     - no*
     - no*
     - yes
   * - py.test 4
     - no**
     - no**
     - no**
     - no**
     - no**
     - no**
     - no**
   * - py.test fixtures
     - no†
     - no†
     - no†
     - no†
     - no†
     - no†
     - no†
   * - | unittest
       | (``@parameterized.expand``)
     - yes
     - yes
     - yes
     - yes
     - yes
     - yes
     - yes
   * - | unittest2
       | (``@parameterized.expand``)
     - yes
     - yes
     - yes
     - yes
     - no§
     - no§
     - yes

§: nose and unittest2 - both of which were last updated in 2015 - sadly do not
appear to support Python 3.10 or 3.11.

\*: `py.test 2 does not appear to work under Python 3 (#71)`__, and
`py.test 3 does not appear to work under Python 3.10 or 3.11 (#154)`__.

\*\*: 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/154
__ 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", 3, 5, 8),
        ])
        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 Python 2.X, 3.5, and 3.6 support?
    As of version 0.9.0, ``parameterized`` no longer supports Python 2.X, 3.5,
    or 3.6.  Previous versions of ``parameterized`` - 0.8.1 being the latest -
    will continue to work, but will not receive any new features or bug fixes.

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.

What happened to ``nose-parameterized``?
    Originally only nose was supported. But now everything is supported, and it
    only made sense to change the name!

            

Raw data

            {
    "_id": null,
    "home_page": "",
    "name": "parameterized",
    "maintainer": "",
    "docs_url": null,
    "requires_python": ">=3.7",
    "maintainer_email": "",
    "keywords": "",
    "author": "",
    "author_email": "David Wolever <david@wolever.net>",
    "download_url": "https://files.pythonhosted.org/packages/ea/49/00c0c0cc24ff4266025a53e41336b79adaa5a4ebfad214f433d623f9865e/parameterized-0.9.0.tar.gz",
    "platform": null,
    "description": "Parameterized testing with any Python test framework\n====================================================\n\n.. image:: https://img.shields.io/pypi/v/parameterized\n    :alt: PyPI\n    :target: https://pypi.org/project/parameterized/\n\n.. image:: https://img.shields.io/pypi/dm/parameterized\n    :alt: PyPI - Downloads\n    :target: https://pypi.org/project/parameterized/\n\n.. image:: https://circleci.com/gh/wolever/parameterized.svg?style=svg\n    :alt: Circle CI\n    :target: https://circleci.com/gh/wolever/parameterized\n\n\nParameterized testing in Python sucks.\n\n``parameterized`` fixes that. For everything. Parameterized testing for nose,\nparameterized testing for py.test, parameterized testing for unittest.\n\n.. code:: python\n\n   # test_math.py\n   from nose.tools import assert_equal\n   from parameterized import parameterized, parameterized_class\n\n   import unittest\n   import math\n\n   @parameterized([\n       (2, 2, 4),\n       (2, 3, 8),\n       (1, 9, 1),\n       (0, 9, 0),\n   ])\n   def test_pow(base, exponent, expected):\n      assert_equal(math.pow(base, exponent), expected)\n\n   class TestMathUnitTest(unittest.TestCase):\n      @parameterized.expand([\n          (\"negative\", -1.5, -2.0),\n          (\"integer\", 1, 1.0),\n          (\"large fraction\", 1.6, 1),\n      ])\n      def test_floor(self, name, input, expected):\n          assert_equal(math.floor(input), expected)\n\n   @parameterized_class(('a', 'b', 'expected_sum', 'expected_product'), [\n      (1, 2, 3, 2),\n      (5, 5, 10, 25),\n   ])\n   class TestMathClass(unittest.TestCase):\n      def test_add(self):\n         assert_equal(self.a + self.b, self.expected_sum)\n\n      def test_multiply(self):\n         assert_equal(self.a * self.b, self.expected_product)\n\n   @parameterized_class([\n      { \"a\": 3, \"expected\": 2 },\n      { \"b\": 5, \"expected\": -4 },\n   ])\n   class TestMathClassDict(unittest.TestCase):\n      a = 1\n      b = 1\n\n      def test_subtract(self):\n         assert_equal(self.a - self.b, self.expected)\n\n\nWith nose (and nose2)::\n\n    $ nosetests -v test_math.py\n    test_floor_0_negative (test_math.TestMathUnitTest) ... ok\n    test_floor_1_integer (test_math.TestMathUnitTest) ... ok\n    test_floor_2_large_fraction (test_math.TestMathUnitTest) ... ok\n    test_math.test_pow(2, 2, 4, {}) ... ok\n    test_math.test_pow(2, 3, 8, {}) ... ok\n    test_math.test_pow(1, 9, 1, {}) ... ok\n    test_math.test_pow(0, 9, 0, {}) ... ok\n    test_add (test_math.TestMathClass_0) ... ok\n    test_multiply (test_math.TestMathClass_0) ... ok\n    test_add (test_math.TestMathClass_1) ... ok\n    test_multiply (test_math.TestMathClass_1) ... ok\n    test_subtract (test_math.TestMathClassDict_0) ... ok\n\n    ----------------------------------------------------------------------\n    Ran 12 tests in 0.015s\n\n    OK\n\nAs the package name suggests, nose is best supported and will be used for all\nfurther examples.\n\n\nWith py.test (version 2.0 and above)::\n\n    $ py.test -v test_math.py\n    ============================= test session starts ==============================\n    platform darwin -- Python 3.6.1, pytest-3.1.3, py-1.4.34, pluggy-0.4.0\n    collecting ... collected 13 items\n\n    test_math.py::test_pow::[0] PASSED\n    test_math.py::test_pow::[1] PASSED\n    test_math.py::test_pow::[2] PASSED\n    test_math.py::test_pow::[3] PASSED\n    test_math.py::TestMathUnitTest::test_floor_0_negative PASSED\n    test_math.py::TestMathUnitTest::test_floor_1_integer PASSED\n    test_math.py::TestMathUnitTest::test_floor_2_large_fraction PASSED\n    test_math.py::TestMathClass_0::test_add PASSED\n    test_math.py::TestMathClass_0::test_multiply PASSED\n    test_math.py::TestMathClass_1::test_add PASSED\n    test_math.py::TestMathClass_1::test_multiply PASSED\n    test_math.py::TestMathClassDict_0::test_subtract PASSED\n    ==================== 12 passed, 4 warnings in 0.16 seconds =====================\n\nWith unittest (and unittest2)::\n\n    $ python -m unittest -v test_math\n    test_floor_0_negative (test_math.TestMathUnitTest) ... ok\n    test_floor_1_integer (test_math.TestMathUnitTest) ... ok\n    test_floor_2_large_fraction (test_math.TestMathUnitTest) ... ok\n    test_add (test_math.TestMathClass_0) ... ok\n    test_multiply (test_math.TestMathClass_0) ... ok\n    test_add (test_math.TestMathClass_1) ... ok\n    test_multiply (test_math.TestMathClass_1) ... ok\n    test_subtract (test_math.TestMathClassDict_0) ... ok\n\n    ----------------------------------------------------------------------\n    Ran 8 tests in 0.001s\n\n    OK\n\n(note: because unittest does not support test decorators, only tests created\nwith ``@parameterized.expand`` will be executed)\n\nWith green::\n\n    $ green test_math.py -vvv\n    test_math\n      TestMathClass_1\n    .   test_method_a\n    .   test_method_b\n      TestMathClass_2\n    .   test_method_a\n    .   test_method_b\n      TestMathClass_3\n    .   test_method_a\n    .   test_method_b\n      TestMathUnitTest\n    .   test_floor_0_negative\n    .   test_floor_1_integer\n    .   test_floor_2_large_fraction\n      TestMathClass_0\n    .   test_add\n    .   test_multiply\n      TestMathClass_1\n    .   test_add\n    .   test_multiply\n      TestMathClassDict_0\n    .   test_subtract\n\n    Ran 12 tests in 0.121s\n\n    OK (passes=9)\n\n\nInstallation\n------------\n\n::\n\n    $ pip install parameterized\n\n\nCompatibility\n-------------\n\n`Yes`__ (mostly).\n\n__ https://app.circleci.com/pipelines/github/wolever/parameterized?branch=master\n\n.. list-table::\n   :header-rows: 1\n   :stub-columns: 1\n\n   * -\n     - Py3.7\n     - Py3.8\n     - Py3.9\n     - Py3.10\n     - Py3.11\n     - PyPy3\n     - ``@mock.patch``\n   * - nose\n     - yes\n     - yes\n     - yes\n     - yes\n     - no\u00a7\n     - no\u00a7\n     - yes\n   * - nose2\n     - yes\n     - yes\n     - yes\n     - yes\n     - yes\n     - yes\n     - yes\n   * - py.test 2\n     - no*\n     - no*\n     - no*\n     - no*\n     - no*\n     - no*\n     - no*\n   * - py.test 3\n     - yes\n     - yes\n     - yes\n     - yes\n     - no*\n     - no*\n     - yes\n   * - py.test 4\n     - no**\n     - no**\n     - no**\n     - no**\n     - no**\n     - no**\n     - no**\n   * - py.test fixtures\n     - no\u2020\n     - no\u2020\n     - no\u2020\n     - no\u2020\n     - no\u2020\n     - no\u2020\n     - no\u2020\n   * - | unittest\n       | (``@parameterized.expand``)\n     - yes\n     - yes\n     - yes\n     - yes\n     - yes\n     - yes\n     - yes\n   * - | unittest2\n       | (``@parameterized.expand``)\n     - yes\n     - yes\n     - yes\n     - yes\n     - no\u00a7\n     - no\u00a7\n     - yes\n\n\u00a7: nose and unittest2 - both of which were last updated in 2015 - sadly do not\nappear to support Python 3.10 or 3.11.\n\n\\*: `py.test 2 does not appear to work under Python 3 (#71)`__, and\n`py.test 3 does not appear to work under Python 3.10 or 3.11 (#154)`__.\n\n\\*\\*: py.test 4 is not yet supported (but coming!) in `issue #34`__\n\n\u2020: py.test fixture support is documented in `issue #81`__\n\n\n__ https://github.com/wolever/parameterized/issues/71\n__ https://github.com/wolever/parameterized/issues/154\n__ https://github.com/wolever/parameterized/issues/34\n__ https://github.com/wolever/parameterized/issues/81\n\nDependencies\n------------\n\n(this section left intentionally blank)\n\n\nExhaustive Usage Examples\n--------------------------\n\nThe ``@parameterized`` and ``@parameterized.expand`` decorators accept a list\nor iterable of tuples or ``param(...)``, or a callable which returns a list or\niterable:\n\n.. code:: python\n\n    from parameterized import parameterized, param\n\n    # A list of tuples\n    @parameterized([\n        (2, 3, 5),\n        (3, 5, 8),\n    ])\n    def test_add(a, b, expected):\n        assert_equal(a + b, expected)\n\n    # A list of params\n    @parameterized([\n        param(\"10\", 10),\n        param(\"10\", 16, base=16),\n    ])\n    def test_int(str_val, expected, base=10):\n        assert_equal(int(str_val, base=base), expected)\n\n    # An iterable of params\n    @parameterized(\n        param.explicit(*json.loads(line))\n        for line in open(\"testcases.jsons\")\n    )\n    def test_from_json_file(...):\n        ...\n\n    # A callable which returns a list of tuples\n    def load_test_cases():\n        return [\n            (\"test1\", ),\n            (\"test2\", ),\n        ]\n    @parameterized(load_test_cases)\n    def test_from_function(name):\n        ...\n\n.. **\n\nNote that, when using an iterator or a generator, all the items will be loaded\ninto memory before the start of the test run (we do this explicitly to ensure\nthat generators are exhausted exactly once in multi-process or multi-threaded\ntesting environments).\n\nThe ``@parameterized`` decorator can be used test class methods, and standalone\nfunctions:\n\n.. code:: python\n\n    from parameterized import parameterized\n\n    class AddTest(object):\n        @parameterized([\n            (2, 3, 5),\n        ])\n        def test_add(self, a, b, expected):\n            assert_equal(a + b, expected)\n\n    @parameterized([\n        (2, 3, 5),\n    ])\n    def test_add(a, b, expected):\n        assert_equal(a + b, expected)\n\n\nAnd ``@parameterized.expand`` can be used to generate test methods in\nsituations where test generators cannot be used (for example, when the test\nclass is a subclass of ``unittest.TestCase``):\n\n.. code:: python\n\n    import unittest\n    from parameterized import parameterized\n\n    class AddTestCase(unittest.TestCase):\n        @parameterized.expand([\n            (\"2 and 3\", 2, 3, 5),\n            (\"3 and 5\", 3, 5, 8),\n        ])\n        def test_add(self, _, a, b, expected):\n            assert_equal(a + b, expected)\n\nWill create the test cases::\n\n    $ nosetests example.py\n    test_add_0_2_and_3 (example.AddTestCase) ... ok\n    test_add_1_3_and_5 (example.AddTestCase) ... ok\n\n    ----------------------------------------------------------------------\n    Ran 2 tests in 0.001s\n\n    OK\n\nNote that ``@parameterized.expand`` works by creating new methods on the test\nclass. If the first parameter is a string, that string will be added to the end\nof the method name. For example, the test case above will generate the methods\n``test_add_0_2_and_3`` and ``test_add_1_3_and_5``.\n\nThe names of the test cases generated by ``@parameterized.expand`` can be\ncustomized using the ``name_func`` keyword argument. The value should\nbe a function which accepts three arguments: ``testcase_func``, ``param_num``,\nand ``params``, and it should return the name of the test case.\n``testcase_func`` will be the function to be tested, ``param_num`` will be the\nindex of the test case parameters in the list of parameters, and ``param``\n(an instance of ``param``) will be the parameters which will be used.\n\n.. code:: python\n\n    import unittest\n    from parameterized import parameterized\n\n    def custom_name_func(testcase_func, param_num, param):\n        return \"%s_%s\" %(\n            testcase_func.__name__,\n            parameterized.to_safe_name(\"_\".join(str(x) for x in param.args)),\n        )\n\n    class AddTestCase(unittest.TestCase):\n        @parameterized.expand([\n            (2, 3, 5),\n            (2, 3, 5),\n        ], name_func=custom_name_func)\n        def test_add(self, a, b, expected):\n            assert_equal(a + b, expected)\n\nWill create the test cases::\n\n    $ nosetests example.py\n    test_add_1_2_3 (example.AddTestCase) ... ok\n    test_add_2_3_5 (example.AddTestCase) ... ok\n\n    ----------------------------------------------------------------------\n    Ran 2 tests in 0.001s\n\n    OK\n\n\nThe ``param(...)`` helper class stores the parameters for one specific test\ncase.  It can be used to pass keyword arguments to test cases:\n\n.. code:: python\n\n    from parameterized import parameterized, param\n\n    @parameterized([\n        param(\"10\", 10),\n        param(\"10\", 16, base=16),\n    ])\n    def test_int(str_val, expected, base=10):\n        assert_equal(int(str_val, base=base), expected)\n\n\nIf test cases have a docstring, the parameters for that test case will be\nappended to the first line of the docstring. This behavior can be controlled\nwith the ``doc_func`` argument:\n\n.. code:: python\n\n    from parameterized import parameterized\n\n    @parameterized([\n        (1, 2, 3),\n        (4, 5, 9),\n    ])\n    def test_add(a, b, expected):\n        \"\"\" Test addition. \"\"\"\n        assert_equal(a + b, expected)\n\n    def my_doc_func(func, num, param):\n        return \"%s: %s with %s\" %(num, func.__name__, param)\n\n    @parameterized([\n        (5, 4, 1),\n        (9, 6, 3),\n    ], doc_func=my_doc_func)\n    def test_subtraction(a, b, expected):\n        assert_equal(a - b, expected)\n\n::\n\n    $ nosetests example.py\n    Test addition. [with a=1, b=2, expected=3] ... ok\n    Test addition. [with a=4, b=5, expected=9] ... ok\n    0: test_subtraction with param(*(5, 4, 1)) ... ok\n    1: test_subtraction with param(*(9, 6, 3)) ... ok\n\n    ----------------------------------------------------------------------\n    Ran 4 tests in 0.001s\n\n    OK\n\nFinally ``@parameterized_class`` parameterizes an entire class, using\neither a list of attributes, or a list of dicts that will be applied to the\nclass:\n\n.. code:: python\n\n    from yourapp.models import User\n    from parameterized import parameterized_class\n\n    @parameterized_class([\n       { \"username\": \"user_1\", \"access_level\": 1 },\n       { \"username\": \"user_2\", \"access_level\": 2, \"expected_status_code\": 404 },\n    ])\n    class TestUserAccessLevel(TestCase):\n       expected_status_code = 200\n\n       def setUp(self):\n          self.client.force_login(User.objects.get(username=self.username)[0])\n\n       def test_url_a(self):\n          response = self.client.get('/url')\n          self.assertEqual(response.status_code, self.expected_status_code)\n\n       def tearDown(self):\n          self.client.logout()\n\n\n    @parameterized_class((\"username\", \"access_level\", \"expected_status_code\"), [\n       (\"user_1\", 1, 200),\n       (\"user_2\", 2, 404)\n    ])\n    class TestUserAccessLevel(TestCase):\n       def setUp(self):\n          self.client.force_login(User.objects.get(username=self.username)[0])\n\n       def test_url_a(self):\n          response = self.client.get(\"/url\")\n          self.assertEqual(response.status_code, self.expected_status_code)\n\n       def tearDown(self):\n          self.client.logout()\n\n\nThe ``@parameterized_class`` decorator accepts a ``class_name_func`` argument,\nwhich controls the name of the parameterized classes generated by\n``@parameterized_class``:\n\n.. code:: python\n\n    from parameterized import parameterized, parameterized_class\n\n    def get_class_name(cls, num, params_dict):\n        # By default the generated class named includes either the \"name\"\n        # parameter (if present), or the first string value. This example shows\n        # multiple parameters being included in the generated class name:\n        return \"%s_%s_%s%s\" %(\n            cls.__name__,\n            num,\n            parameterized.to_safe_name(params_dict['a']),\n            parameterized.to_safe_name(params_dict['b']),\n        )\n\n    @parameterized_class([\n       { \"a\": \"hello\", \"b\": \" world!\", \"expected\": \"hello world!\" },\n       { \"a\": \"say \", \"b\": \" cheese :)\", \"expected\": \"say cheese :)\" },\n    ], class_name_func=get_class_name)\n    class TestConcatenation(TestCase):\n      def test_concat(self):\n          self.assertEqual(self.a + self.b, self.expected)\n\n::\n\n    $ nosetests -v test_math.py\n    test_concat (test_concat.TestConcatenation_0_hello_world_) ... ok\n    test_concat (test_concat.TestConcatenation_0_say_cheese__) ... ok\n\n\n\nUsing with Single Parameters\n............................\n\nIf a test function only accepts one parameter and the value is not iterable,\nthen it is possible to supply a list of values without wrapping each one in a\ntuple:\n\n.. code:: python\n\n   @parameterized([1, 2, 3])\n   def test_greater_than_zero(value):\n      assert value > 0\n\nNote, however, that if the single parameter *is* iterable (such as a list or\ntuple), then it *must* be wrapped in a tuple, list, or the ``param(...)``\nhelper:\n\n.. code:: python\n\n   @parameterized([\n      ([1, 2, 3], ),\n      ([3, 3], ),\n      ([6], ),\n   ])\n   def test_sums_to_6(numbers):\n      assert sum(numbers) == 6\n\n(note, also, that Python requires single element tuples to be defined with a\ntrailing comma: ``(foo, )``)\n\n\nUsing with ``@mock.patch``\n..........................\n\n``parameterized`` can be used with ``mock.patch``, but the argument ordering\ncan be confusing. The ``@mock.patch(...)`` decorator must come *below* the\n``@parameterized(...)``, and the mocked parameters must come *last*:\n\n.. code:: python\n\n   @mock.patch(\"os.getpid\")\n   class TestOS(object):\n      @parameterized(...)\n      @mock.patch(\"os.fdopen\")\n      @mock.patch(\"os.umask\")\n      def test_method(self, param1, param2, ..., mock_umask, mock_fdopen, mock_getpid):\n         ...\n\nNote: the same holds true when using ``@parameterized.expand``.\n\n\nMigrating from ``nose-parameterized`` to ``parameterized``\n----------------------------------------------------------\n\nTo migrate a codebase from ``nose-parameterized`` to ``parameterized``:\n\n1. Update your requirements file, replacing ``nose-parameterized`` with\n   ``parameterized``.\n\n2. Replace all references to ``nose_parameterized`` with ``parameterized``::\n\n    $ perl -pi -e 's/nose_parameterized/parameterized/g' your-codebase/\n\n3. You're done!\n\n\nFAQ\n---\n\nWhat happened to Python 2.X, 3.5, and 3.6 support?\n    As of version 0.9.0, ``parameterized`` no longer supports Python 2.X, 3.5,\n    or 3.6.  Previous versions of ``parameterized`` - 0.8.1 being the latest -\n    will continue to work, but will not receive any new features or bug fixes.\n\nWhat do you mean when you say \"nose is best supported\"?\n    There are small caveates with ``py.test`` and ``unittest``: ``py.test``\n    does not show the parameter values (ex, it will show ``test_add[0]``\n    instead of ``test_add[1, 2, 3]``), and ``unittest``/``unittest2`` do not\n    support test generators so ``@parameterized.expand`` must be used.\n\nWhy not use ``@pytest.mark.parametrize``?\n    Because spelling is difficult. Also, ``parameterized`` doesn't require you\n    to repeat argument names, and (using ``param``) it supports optional\n    keyword arguments.\n\nWhy do I get an ``AttributeError: 'function' object has no attribute 'expand'`` with ``@parameterized.expand``?\n    You've likely installed the ``parametrized`` (note the missing *e*)\n    package. Use ``parameterized`` (with the *e*) instead and you'll be all\n    set.\n\nWhat happened to ``nose-parameterized``?\n    Originally only nose was supported. But now everything is supported, and it\n    only made sense to change the name!\n",
    "bugtrack_url": null,
    "license": "FreeBSD",
    "summary": "Parameterized testing with any Python test framework",
    "version": "0.9.0",
    "split_keywords": [],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "002f804f58f0b856ab3bf21617cccf5b39206e6c4c94c2cd227bde125ea6105f",
                "md5": "cfce2b56c952afb5a64ba988722352e7",
                "sha256": "4e0758e3d41bea3bbd05ec14fc2c24736723f243b28d702081aef438c9372b1b"
            },
            "downloads": -1,
            "filename": "parameterized-0.9.0-py2.py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "cfce2b56c952afb5a64ba988722352e7",
            "packagetype": "bdist_wheel",
            "python_version": "py2.py3",
            "requires_python": ">=3.7",
            "size": 20475,
            "upload_time": "2023-03-27T02:01:09",
            "upload_time_iso_8601": "2023-03-27T02:01:09.310468Z",
            "url": "https://files.pythonhosted.org/packages/00/2f/804f58f0b856ab3bf21617cccf5b39206e6c4c94c2cd227bde125ea6105f/parameterized-0.9.0-py2.py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "ea4900c0c0cc24ff4266025a53e41336b79adaa5a4ebfad214f433d623f9865e",
                "md5": "ed1bee2fb5d9044688d8503bdda9e6f3",
                "sha256": "7fc905272cefa4f364c1a3429cbbe9c0f98b793988efb5bf90aac80f08db09b1"
            },
            "downloads": -1,
            "filename": "parameterized-0.9.0.tar.gz",
            "has_sig": false,
            "md5_digest": "ed1bee2fb5d9044688d8503bdda9e6f3",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.7",
            "size": 24351,
            "upload_time": "2023-03-27T02:01:11",
            "upload_time_iso_8601": "2023-03-27T02:01:11.592497Z",
            "url": "https://files.pythonhosted.org/packages/ea/49/00c0c0cc24ff4266025a53e41336b79adaa5a4ebfad214f433d623f9865e/parameterized-0.9.0.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2023-03-27 02:01:11",
    "github": false,
    "gitlab": false,
    "bitbucket": false,
    "lcname": "parameterized"
}
        
Elapsed time: 0.04955s