unittest-parametrize


Nameunittest-parametrize JSON
Version 1.3.0 PyPI version JSON
download
home_pagehttps://github.com/adamchainz/unittest-parametrize
SummaryParametrize tests within unittest TestCases.
upload_time2023-07-10 12:25:51
maintainer
docs_urlNone
authorAdam Johnson
requires_python>=3.8
licenseMIT
keywords unittest
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            ====================
unittest-parametrize
====================

.. image:: https://img.shields.io/github/actions/workflow/status/adamchainz/unittest-parametrize/main.yml?branch=main&style=for-the-badge
   :target: https://github.com/adamchainz/unittest-parametrize/actions?workflow=CI

.. image:: https://img.shields.io/pypi/v/unittest-parametrize.svg?style=for-the-badge
   :target: https://pypi.org/project/unittest-parametrize/

.. image:: https://img.shields.io/badge/code%20style-black-000000.svg?style=for-the-badge
   :target: https://github.com/psf/black

.. image:: https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit&logoColor=white&style=for-the-badge
   :target: https://github.com/pre-commit/pre-commit
   :alt: pre-commit

Parametrize tests within unittest TestCases.

Installation
============

Install with:

.. code-block:: bash

    python -m pip install unittest-parametrize

Python 3.8 to 3.12 supported.

----

**Testing a Django project?**
Check out my book `Speed Up Your Django Tests <https://adamchainz.gumroad.com/l/suydt>`__ which covers loads of recommendations to write faster, more accurate tests.

----

Usage
=====

The API mirrors |@pytest.mark.parametrize|__ as much as possible.
(Even the name `parametrize <https://en.wiktionary.org/wiki/parametrize#English>`__ over the slightly more common `parameterize <https://en.wiktionary.org/wiki/parameterize#English>`__ with an extra “e”.
Don’t get caught out by that…)

.. |@pytest.mark.parametrize| replace:: ``@pytest.mark.parametrize``
__ https://docs.pytest.org/en/stable/how-to/parametrize.html#parametrize-basics

There are two steps to parametrize a test case:

1. Use ``ParametrizedTestCase`` in the base classes for your test case.
2. Apply ``@parametrize`` to any tests for parametrization.
   This decorator takes (at least):

   * the argument names to parametrize, as comma-separated string
   * a list of parameter tuples to create individual tests for

Here’s a basic example:

.. code-block:: python

    from unittest_parametrize import parametrize
    from unittest_parametrize import ParametrizedTestCase


    class SquareTests(ParametrizedTestCase):
        @parametrize(
            "x,expected",
            [
                (1, 1),
                (2, 4),
            ],
        )
        def test_square(self, x: int, expected: int) -> None:
            self.assertEqual(x**2, expected)

``@parametrize`` modifies the class at definition time with Python’s |__init_subclass__ hook|__.
It removes the original test method and creates wrapped copies with individual names.
Thus the parametrization should work regardless of the test runner you use (be it unittest, Django’s test runner, pytest, etc.).

.. |__init_subclass__ hook| replace:: ``__init_subclass__`` hook
__ https://docs.python.org/3/reference/datamodel.html#object.__init_subclass__

Provide argument names as separate strings
------------------------------------------

You can provide argument names as a sequence of strings instead:

.. code-block:: python

    from unittest_parametrize import parametrize
    from unittest_parametrize import ParametrizedTestCase


    class SquareTests(ParametrizedTestCase):
        @parametrize(
            ("x", "expected"),
            [
                (1, 1),
                (2, 4),
            ],
        )
        def test_square(self, x: int, expected: int) -> None:
            self.assertEqual(x**2, expected)


Use ``ParametrizedTestCase`` in your base test case class
---------------------------------------------------------

``ParametrizedTestCase`` does nothing if there aren’t any ``@parametrize``-decorated tests within a class.
Therefore you can include it in your project’s base test case class so that ``@parametrize`` works immediately in all test cases.

For example, within a Django project, you can create a set of project-specific base test case classes extending `those provided by Django <https://docs.djangoproject.com/en/stable/topics/testing/tools/#provided-test-case-classes>`__.
You can do this in a module like ``example.test``, and use the base classes throughout your test suite.
To add ``ParametrizedTestCase`` to all your copies, use it in a custom ``SimpleTestCase`` and then mixin to others using multiple inheritance like so:

.. code-block:: python

    from django import test
    from unittest_parametrize import ParametrizedTestCase


    class SimpleTestCase(ParametrizedTestCase, test.SimpleTestCase):
        pass


    class TestCase(SimpleTestCase, test.TestCase):
        pass


    class TransactionTestCase(SimpleTestCase, test.TransactionTestCase):
        pass


    class LiveServerTestCase(SimpleTestCase, test.LiveServerTestCase):
        pass

Custom test name suffixes
-------------------------

By default, test names are extended with an index, starting at zero.
You can see these names when running the tests:

.. code-block:: console

    $ python -m unittest t.py -v
    test_square_0 (t.SquareTests.test_square_0) ... ok
    test_square_1 (t.SquareTests.test_square_1) ... ok

    ----------------------------------------------------------------------
    Ran 2 tests in 0.000s

    OK

You can customize these names by passing ``param`` objects, which contain the arguments and an optional ID for the suffix:

.. code-block:: python

    from unittest_parametrize import param
    from unittest_parametrize import parametrize
    from unittest_parametrize import ParametrizedTestCase


    class SquareTests(ParametrizedTestCase):
        @parametrize(
            "x,expected",
            [
                param(1, 1, id="one"),
                param(2, 4, id="two"),
            ],
        )
        def test_square(self, x: int, expected: int) -> None:
            self.assertEqual(x**2, expected)

Yielding perhaps more natural names:

.. code-block:: console

    $ python -m unittest t.py -v
    test_square_one (t.SquareTests.test_square_one) ... ok
    test_square_two (t.SquareTests.test_square_two) ... ok

    ----------------------------------------------------------------------
    Ran 2 tests in 0.000s

    OK

Parameter IDs should be valid Python identifier suffixes.

Since parameter IDs are optional, you can provide them only for some tests:

.. code-block:: python

    from unittest_parametrize import param
    from unittest_parametrize import parametrize
    from unittest_parametrize import ParametrizedTestCase


    class SquareTests(ParametrizedTestCase):
        @parametrize(
            "x,expected",
            [
                param(1, 1),
                param(20, 400, id="large"),
            ],
        )
        def test_square(self, x: int, expected: int) -> None:
            self.assertEqual(x**2, expected)

ID-free ``param``\s fall back to the default index suffixes:

.. code-block:: console

    $ python -m unittest t.py -v
    test_square_0 (example.SquareTests.test_square_0) ... ok
    test_square_large (example.SquareTests.test_square_large) ... ok

    ----------------------------------------------------------------------
    Ran 2 tests in 0.000s

    OK

Alternatively, you can provide the id’s separately with the ``ids`` argument:

.. code-block:: python

    from unittest_parametrize import parametrize
    from unittest_parametrize import ParametrizedTestCase


    class SquareTests(ParametrizedTestCase):
        @parametrize(
            "x,expected",
            [
                (1, 1),
                (2, 4),
            ],
            ids=["one", "two"],
        )
        def test_square(self, x: int, expected: int) -> None:
            self.assertEqual(x**2, expected)

Use with other test decorators
------------------------------

``@parametrize`` tries to ensure it is the top-most (outermost) decorator.
This limitation exists to ensure that other decorators apply to each parametrized test.
So decorators like ``@mock.patch`` need be beneath ``@parametrize``:

.. code-block:: python

    from unittest import mock
    from unittest_parametrize import parametrize
    from unittest_parametrize import ParametrizedTestCase


    class CarpentryTests(ParametrizedTestCase):
        @parametrize(
            "nails",
            [(11,), (17,)],
        )
        @mock.patch("example.hammer", autospec=True)
        def test_nail_a_board(self, mock_hammer, nails):
            ...

Also note that due to how ``mock.patch`` always adds positional arguments at the start, the parametrized arguments must come last.
``@parametrize`` always adds parameters as keyword arguments, so you can also use `keyword-only syntax <https://peps.python.org/pep-3102/>`__ for parametrized arguments:

.. code-block:: python

    # ...
    def test_nail_a_board(self, mock_hammer, *, nails):
        ...

Multiple ``@parametrize`` decorators
------------------------------------

``@parametrize`` is not stackable.
To create a cross-product of tests, you can use nested list comprehensions:

.. code-block:: python

    from unittest_parametrize import parametrize
    from unittest_parametrize import ParametrizedTestCase


    class RocketTests(ParametrizedTestCase):
        @parametrize(
            "use_ions,hyperdrive_level",
            [
                (use_ions, hyperdrive_level)
                for use_ions in [True, False]
                for hyperdrive_level in [0, 1, 2]
            ],
        )
        def test_takeoff(self, use_ions, hyperdrive_level) -> None:
            ...

The above creates 2 * 3 = 6 versions of ``test_takeoff``.

For larger combinations, |itertools.product()|__ may be more readable:

.. |itertools.product()| replace:: ``itertools.product()``
__ https://docs.python.org/3/library/itertools.html#itertools.product

.. code-block:: python

    from itertools import product
    from unittest_parametrize import parametrize
    from unittest_parametrize import ParametrizedTestCase


    class RocketTests(ParametrizedTestCase):
        @parametrize(
            "use_ions,hyperdrive_level,nose_colour",
            list(
                product(
                    [True, False],
                    [0, 1, 2],
                    ["red", "yellow"],
                )
            ),
        )
        def test_takeoff(self, use_ions, hyperdrive_level, nose_colour) -> None:
            ...

The above creates 2 * 3 * 2 = 12 versions of ``test_takeoff``.

Parametrizing multiple tests in a test case
-------------------------------------------

``@parametrize`` only works as a function decorator, not a class decorator.
To parametrize all tests within a test case, create a separate decorator and apply it to each method:

.. code-block:: python

    from unittest_parametrize import parametrize
    from unittest_parametrize import ParametrizedTestCase


    parametrize_race = parametrize(
        "race",
        [("Human",), ("Halfling",), ("Dwarf",), ("Elf",)],
    )


    class StatsTests(ParametrizedTestCase):
        @parametrize_race
        def test_strength(self, race: str) -> None:
            ...

        @parametrize_race
        def test_dexterity(self, race: str) -> None:
            ...

        ...

History
=======

When I started writing unit tests, I learned to use `DDT (Data-Driven Tests) <https://ddt.readthedocs.io/en/latest/>`__ for parametrizing tests.
It works, but the docs are a bit thin, and the API a little obscure (what does ``@ddt`` stand for again?).

Later when picking up pytest, I learned to use its `parametrization API <https://docs.pytest.org/en/stable/how-to/parametrize.html>`__.
It’s legible and flexible, but it doesn’t work with unittest test cases, which Django’s test tooling provides.

So, until the creation of this package, I was using `parameterized <https://pypi.org/project/parameterized/>`__ on my (Django) test cases.
This package supports parametrization across multiple test runners, though most of them are “legacy” by now.

I created unittest-parametrize as a smaller alternative to *parameterized*, with these goals:

1. Only support unittest test cases.
   For other types of test, you can use pytest’s parametrization.

2. Avoid any custom test runner support.
   Modifying the class at definition time means that all test runners will see the tests the same.

3. Use modern Python features like ``__init_subclass__``.

4. Have full type hint coverage.
   You shouldn’t find unittest-parametrize a blocker when adopting Mypy with strict mode on.

5. Use the name “parametrize” rather than “parameterize”.
   This unification of spelling with pytest should help reduce confusion around the extra “e”.

Thanks to the creators and maintainers of ddt, parameterized, and pytest for their hard work.

Why not subtests?
-----------------

|TestCase.subTest()|__ is unittest’s built-in “parametrization” solution.
You use it in a loop within a single test method:

.. |TestCase.subTest()| replace:: ``TestCase.subTest()``
__ https://docs.python.org/3/library/unittest.html#unittest.TestCase.subTest

.. code-block:: python

    from unittest import TestCase


    class SquareTests(TestCase):
        def test_square(self):
            tests = [
                (1, 1),
                (2, 4),
            ]
            for x, expected in tests:
                with self.subTest(x=x):
                    self.assertEqual(x**2, expected)

This approach crams multiple actual tests into one test method, with several consequences:

* If a subtest fails, it prevents the next subtests from running.
  Thus, failures are harder to debug, since each test run can only give you partial information.

* Subtests can leak state.
  Without correct isolation, they may not test what they appear to.

* Subtests cannot be reordered by tools that detect state leakage, like `pytest-randomly <https://github.com/pytest-dev/pytest-randomly>`__.

* Subtests skew test timings, since the test method runs multiple tests.

* Everything is indented two extra levels for the loop and context manager.

Parametrization avoids all these issues by creating individual test methods.

            

Raw data

            {
    "_id": null,
    "home_page": "https://github.com/adamchainz/unittest-parametrize",
    "name": "unittest-parametrize",
    "maintainer": "",
    "docs_url": null,
    "requires_python": ">=3.8",
    "maintainer_email": "",
    "keywords": "unittest",
    "author": "Adam Johnson",
    "author_email": "me@adamj.eu",
    "download_url": "https://files.pythonhosted.org/packages/16/d1/7aad06aab26bca4ddcb8ea47a34c3af377fce9f9f8ded8c264710b45c38f/unittest_parametrize-1.3.0.tar.gz",
    "platform": null,
    "description": "====================\nunittest-parametrize\n====================\n\n.. image:: https://img.shields.io/github/actions/workflow/status/adamchainz/unittest-parametrize/main.yml?branch=main&style=for-the-badge\n   :target: https://github.com/adamchainz/unittest-parametrize/actions?workflow=CI\n\n.. image:: https://img.shields.io/pypi/v/unittest-parametrize.svg?style=for-the-badge\n   :target: https://pypi.org/project/unittest-parametrize/\n\n.. image:: https://img.shields.io/badge/code%20style-black-000000.svg?style=for-the-badge\n   :target: https://github.com/psf/black\n\n.. image:: https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit&logoColor=white&style=for-the-badge\n   :target: https://github.com/pre-commit/pre-commit\n   :alt: pre-commit\n\nParametrize tests within unittest TestCases.\n\nInstallation\n============\n\nInstall with:\n\n.. code-block:: bash\n\n    python -m pip install unittest-parametrize\n\nPython 3.8 to 3.12 supported.\n\n----\n\n**Testing a Django project?**\nCheck out my book `Speed Up Your Django Tests <https://adamchainz.gumroad.com/l/suydt>`__ which covers loads of recommendations to write faster, more accurate tests.\n\n----\n\nUsage\n=====\n\nThe API mirrors |@pytest.mark.parametrize|__ as much as possible.\n(Even the name `parametrize <https://en.wiktionary.org/wiki/parametrize#English>`__ over the slightly more common `parameterize <https://en.wiktionary.org/wiki/parameterize#English>`__ with an extra \u201ce\u201d.\nDon\u2019t get caught out by that\u2026)\n\n.. |@pytest.mark.parametrize| replace:: ``@pytest.mark.parametrize``\n__ https://docs.pytest.org/en/stable/how-to/parametrize.html#parametrize-basics\n\nThere are two steps to parametrize a test case:\n\n1. Use ``ParametrizedTestCase`` in the base classes for your test case.\n2. Apply ``@parametrize`` to any tests for parametrization.\n   This decorator takes (at least):\n\n   * the argument names to parametrize, as comma-separated string\n   * a list of parameter tuples to create individual tests for\n\nHere\u2019s a basic example:\n\n.. code-block:: python\n\n    from unittest_parametrize import parametrize\n    from unittest_parametrize import ParametrizedTestCase\n\n\n    class SquareTests(ParametrizedTestCase):\n        @parametrize(\n            \"x,expected\",\n            [\n                (1, 1),\n                (2, 4),\n            ],\n        )\n        def test_square(self, x: int, expected: int) -> None:\n            self.assertEqual(x**2, expected)\n\n``@parametrize`` modifies the class at definition time with Python\u2019s |__init_subclass__ hook|__.\nIt removes the original test method and creates wrapped copies with individual names.\nThus the parametrization should work regardless of the test runner you use (be it unittest, Django\u2019s test runner, pytest, etc.).\n\n.. |__init_subclass__ hook| replace:: ``__init_subclass__`` hook\n__ https://docs.python.org/3/reference/datamodel.html#object.__init_subclass__\n\nProvide argument names as separate strings\n------------------------------------------\n\nYou can provide argument names as a sequence of strings instead:\n\n.. code-block:: python\n\n    from unittest_parametrize import parametrize\n    from unittest_parametrize import ParametrizedTestCase\n\n\n    class SquareTests(ParametrizedTestCase):\n        @parametrize(\n            (\"x\", \"expected\"),\n            [\n                (1, 1),\n                (2, 4),\n            ],\n        )\n        def test_square(self, x: int, expected: int) -> None:\n            self.assertEqual(x**2, expected)\n\n\nUse ``ParametrizedTestCase`` in your base test case class\n---------------------------------------------------------\n\n``ParametrizedTestCase`` does nothing if there aren\u2019t any ``@parametrize``-decorated tests within a class.\nTherefore you can include it in your project\u2019s base test case class so that ``@parametrize`` works immediately in all test cases.\n\nFor example, within a Django project, you can create a set of project-specific base test case classes extending `those provided by Django <https://docs.djangoproject.com/en/stable/topics/testing/tools/#provided-test-case-classes>`__.\nYou can do this in a module like ``example.test``, and use the base classes throughout your test suite.\nTo add ``ParametrizedTestCase`` to all your copies, use it in a custom ``SimpleTestCase`` and then mixin to others using multiple inheritance like so:\n\n.. code-block:: python\n\n    from django import test\n    from unittest_parametrize import ParametrizedTestCase\n\n\n    class SimpleTestCase(ParametrizedTestCase, test.SimpleTestCase):\n        pass\n\n\n    class TestCase(SimpleTestCase, test.TestCase):\n        pass\n\n\n    class TransactionTestCase(SimpleTestCase, test.TransactionTestCase):\n        pass\n\n\n    class LiveServerTestCase(SimpleTestCase, test.LiveServerTestCase):\n        pass\n\nCustom test name suffixes\n-------------------------\n\nBy default, test names are extended with an index, starting at zero.\nYou can see these names when running the tests:\n\n.. code-block:: console\n\n    $ python -m unittest t.py -v\n    test_square_0 (t.SquareTests.test_square_0) ... ok\n    test_square_1 (t.SquareTests.test_square_1) ... ok\n\n    ----------------------------------------------------------------------\n    Ran 2 tests in 0.000s\n\n    OK\n\nYou can customize these names by passing ``param`` objects, which contain the arguments and an optional ID for the suffix:\n\n.. code-block:: python\n\n    from unittest_parametrize import param\n    from unittest_parametrize import parametrize\n    from unittest_parametrize import ParametrizedTestCase\n\n\n    class SquareTests(ParametrizedTestCase):\n        @parametrize(\n            \"x,expected\",\n            [\n                param(1, 1, id=\"one\"),\n                param(2, 4, id=\"two\"),\n            ],\n        )\n        def test_square(self, x: int, expected: int) -> None:\n            self.assertEqual(x**2, expected)\n\nYielding perhaps more natural names:\n\n.. code-block:: console\n\n    $ python -m unittest t.py -v\n    test_square_one (t.SquareTests.test_square_one) ... ok\n    test_square_two (t.SquareTests.test_square_two) ... ok\n\n    ----------------------------------------------------------------------\n    Ran 2 tests in 0.000s\n\n    OK\n\nParameter IDs should be valid Python identifier suffixes.\n\nSince parameter IDs are optional, you can provide them only for some tests:\n\n.. code-block:: python\n\n    from unittest_parametrize import param\n    from unittest_parametrize import parametrize\n    from unittest_parametrize import ParametrizedTestCase\n\n\n    class SquareTests(ParametrizedTestCase):\n        @parametrize(\n            \"x,expected\",\n            [\n                param(1, 1),\n                param(20, 400, id=\"large\"),\n            ],\n        )\n        def test_square(self, x: int, expected: int) -> None:\n            self.assertEqual(x**2, expected)\n\nID-free ``param``\\s fall back to the default index suffixes:\n\n.. code-block:: console\n\n    $ python -m unittest t.py -v\n    test_square_0 (example.SquareTests.test_square_0) ... ok\n    test_square_large (example.SquareTests.test_square_large) ... ok\n\n    ----------------------------------------------------------------------\n    Ran 2 tests in 0.000s\n\n    OK\n\nAlternatively, you can provide the id\u2019s separately with the ``ids`` argument:\n\n.. code-block:: python\n\n    from unittest_parametrize import parametrize\n    from unittest_parametrize import ParametrizedTestCase\n\n\n    class SquareTests(ParametrizedTestCase):\n        @parametrize(\n            \"x,expected\",\n            [\n                (1, 1),\n                (2, 4),\n            ],\n            ids=[\"one\", \"two\"],\n        )\n        def test_square(self, x: int, expected: int) -> None:\n            self.assertEqual(x**2, expected)\n\nUse with other test decorators\n------------------------------\n\n``@parametrize`` tries to ensure it is the top-most (outermost) decorator.\nThis limitation exists to ensure that other decorators apply to each parametrized test.\nSo decorators like ``@mock.patch`` need be beneath ``@parametrize``:\n\n.. code-block:: python\n\n    from unittest import mock\n    from unittest_parametrize import parametrize\n    from unittest_parametrize import ParametrizedTestCase\n\n\n    class CarpentryTests(ParametrizedTestCase):\n        @parametrize(\n            \"nails\",\n            [(11,), (17,)],\n        )\n        @mock.patch(\"example.hammer\", autospec=True)\n        def test_nail_a_board(self, mock_hammer, nails):\n            ...\n\nAlso note that due to how ``mock.patch`` always adds positional arguments at the start, the parametrized arguments must come last.\n``@parametrize`` always adds parameters as keyword arguments, so you can also use `keyword-only syntax <https://peps.python.org/pep-3102/>`__ for parametrized arguments:\n\n.. code-block:: python\n\n    # ...\n    def test_nail_a_board(self, mock_hammer, *, nails):\n        ...\n\nMultiple ``@parametrize`` decorators\n------------------------------------\n\n``@parametrize`` is not stackable.\nTo create a cross-product of tests, you can use nested list comprehensions:\n\n.. code-block:: python\n\n    from unittest_parametrize import parametrize\n    from unittest_parametrize import ParametrizedTestCase\n\n\n    class RocketTests(ParametrizedTestCase):\n        @parametrize(\n            \"use_ions,hyperdrive_level\",\n            [\n                (use_ions, hyperdrive_level)\n                for use_ions in [True, False]\n                for hyperdrive_level in [0, 1, 2]\n            ],\n        )\n        def test_takeoff(self, use_ions, hyperdrive_level) -> None:\n            ...\n\nThe above creates 2 * 3 = 6 versions of ``test_takeoff``.\n\nFor larger combinations, |itertools.product()|__ may be more readable:\n\n.. |itertools.product()| replace:: ``itertools.product()``\n__ https://docs.python.org/3/library/itertools.html#itertools.product\n\n.. code-block:: python\n\n    from itertools import product\n    from unittest_parametrize import parametrize\n    from unittest_parametrize import ParametrizedTestCase\n\n\n    class RocketTests(ParametrizedTestCase):\n        @parametrize(\n            \"use_ions,hyperdrive_level,nose_colour\",\n            list(\n                product(\n                    [True, False],\n                    [0, 1, 2],\n                    [\"red\", \"yellow\"],\n                )\n            ),\n        )\n        def test_takeoff(self, use_ions, hyperdrive_level, nose_colour) -> None:\n            ...\n\nThe above creates 2 * 3 * 2 = 12 versions of ``test_takeoff``.\n\nParametrizing multiple tests in a test case\n-------------------------------------------\n\n``@parametrize`` only works as a function decorator, not a class decorator.\nTo parametrize all tests within a test case, create a separate decorator and apply it to each method:\n\n.. code-block:: python\n\n    from unittest_parametrize import parametrize\n    from unittest_parametrize import ParametrizedTestCase\n\n\n    parametrize_race = parametrize(\n        \"race\",\n        [(\"Human\",), (\"Halfling\",), (\"Dwarf\",), (\"Elf\",)],\n    )\n\n\n    class StatsTests(ParametrizedTestCase):\n        @parametrize_race\n        def test_strength(self, race: str) -> None:\n            ...\n\n        @parametrize_race\n        def test_dexterity(self, race: str) -> None:\n            ...\n\n        ...\n\nHistory\n=======\n\nWhen I started writing unit tests, I learned to use `DDT (Data-Driven Tests) <https://ddt.readthedocs.io/en/latest/>`__ for parametrizing tests.\nIt works, but the docs are a bit thin, and the API a little obscure (what does ``@ddt`` stand for again?).\n\nLater when picking up pytest, I learned to use its `parametrization API <https://docs.pytest.org/en/stable/how-to/parametrize.html>`__.\nIt\u2019s legible and flexible, but it doesn\u2019t work with unittest test cases, which Django\u2019s test tooling provides.\n\nSo, until the creation of this package, I was using `parameterized <https://pypi.org/project/parameterized/>`__ on my (Django) test cases.\nThis package supports parametrization across multiple test runners, though most of them are \u201clegacy\u201d by now.\n\nI created unittest-parametrize as a smaller alternative to *parameterized*, with these goals:\n\n1. Only support unittest test cases.\n   For other types of test, you can use pytest\u2019s parametrization.\n\n2. Avoid any custom test runner support.\n   Modifying the class at definition time means that all test runners will see the tests the same.\n\n3. Use modern Python features like ``__init_subclass__``.\n\n4. Have full type hint coverage.\n   You shouldn\u2019t find unittest-parametrize a blocker when adopting Mypy with strict mode on.\n\n5. Use the name \u201cparametrize\u201d rather than \u201cparameterize\u201d.\n   This unification of spelling with pytest should help reduce confusion around the extra \u201ce\u201d.\n\nThanks to the creators and maintainers of ddt, parameterized, and pytest for their hard work.\n\nWhy not subtests?\n-----------------\n\n|TestCase.subTest()|__ is unittest\u2019s built-in \u201cparametrization\u201d solution.\nYou use it in a loop within a single test method:\n\n.. |TestCase.subTest()| replace:: ``TestCase.subTest()``\n__ https://docs.python.org/3/library/unittest.html#unittest.TestCase.subTest\n\n.. code-block:: python\n\n    from unittest import TestCase\n\n\n    class SquareTests(TestCase):\n        def test_square(self):\n            tests = [\n                (1, 1),\n                (2, 4),\n            ]\n            for x, expected in tests:\n                with self.subTest(x=x):\n                    self.assertEqual(x**2, expected)\n\nThis approach crams multiple actual tests into one test method, with several consequences:\n\n* If a subtest fails, it prevents the next subtests from running.\n  Thus, failures are harder to debug, since each test run can only give you partial information.\n\n* Subtests can leak state.\n  Without correct isolation, they may not test what they appear to.\n\n* Subtests cannot be reordered by tools that detect state leakage, like `pytest-randomly <https://github.com/pytest-dev/pytest-randomly>`__.\n\n* Subtests skew test timings, since the test method runs multiple tests.\n\n* Everything is indented two extra levels for the loop and context manager.\n\nParametrization avoids all these issues by creating individual test methods.\n",
    "bugtrack_url": null,
    "license": "MIT",
    "summary": "Parametrize tests within unittest TestCases.",
    "version": "1.3.0",
    "project_urls": {
        "Changelog": "https://github.com/adamchainz/unittest-parametrize/blob/main/CHANGELOG.rst",
        "Homepage": "https://github.com/adamchainz/unittest-parametrize",
        "Mastodon": "https://fosstodon.org/@adamchainz",
        "Twitter": "https://twitter.com/adamchainz"
    },
    "split_keywords": [
        "unittest"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "a1bb63c8ec48b2bb4d967176a356574269638d3300fc5420d3d1858b1ed19efb",
                "md5": "a0f2a45cf31c9cb63707ade225071c25",
                "sha256": "c5cf14b258f6d10ce1f0b32fc354cf878857a1f1d1f50bc0861fbb6142e4b3fa"
            },
            "downloads": -1,
            "filename": "unittest_parametrize-1.3.0-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "a0f2a45cf31c9cb63707ade225071c25",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.8",
            "size": 8107,
            "upload_time": "2023-07-10T12:25:49",
            "upload_time_iso_8601": "2023-07-10T12:25:49.536907Z",
            "url": "https://files.pythonhosted.org/packages/a1/bb/63c8ec48b2bb4d967176a356574269638d3300fc5420d3d1858b1ed19efb/unittest_parametrize-1.3.0-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "16d17aad06aab26bca4ddcb8ea47a34c3af377fce9f9f8ded8c264710b45c38f",
                "md5": "0f13531cc91b4a11ab823334102d7e4f",
                "sha256": "6ed8712d106446acf60f3c369cfe3eeffa9e33e338df4073681052059b0d55bb"
            },
            "downloads": -1,
            "filename": "unittest_parametrize-1.3.0.tar.gz",
            "has_sig": false,
            "md5_digest": "0f13531cc91b4a11ab823334102d7e4f",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.8",
            "size": 12444,
            "upload_time": "2023-07-10T12:25:51",
            "upload_time_iso_8601": "2023-07-10T12:25:51.443779Z",
            "url": "https://files.pythonhosted.org/packages/16/d1/7aad06aab26bca4ddcb8ea47a34c3af377fce9f9f8ded8c264710b45c38f/unittest_parametrize-1.3.0.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2023-07-10 12:25:51",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "adamchainz",
    "github_project": "unittest-parametrize",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": true,
    "tox": true,
    "lcname": "unittest-parametrize"
}
        
Elapsed time: 0.12781s