Skip to content

A pytest library for parametrizing tests using type hints.

License

Notifications You must be signed in to change notification settings

skeletorXVI/pytest-parametrization-annotation

Repository files navigation

Package to use type annotations for parametrization in pytest

codecov Code style: black

This package allows the declaration of parametrization using type annotations and definition of test cases using a decorator.

The primary reason of using this package over the standard pytest.mark.parametrize is readability and maintainability of test cases especially if many test cases are defined for a single test function. In addition, error messages are more informative compared to those provided by pytest.mark.parametrize.

Install pytest-parametrization-annotation

pip install pytest-parametrization-annotation

Usage

Defining a test function parameter as parametrized

To define a parameter as parametrized, use the Parametrized class or an instance of the Parametrized class as an annotation.

from typing import Annotated
from pytest_parametrization_annotation import Parametrized


# Both definitions are treated the same
def test(a: Annotated[int, Parametrized], b: Annotated[int, Parametrized()]) -> None:
    ...

By default, parameters annotated in this way are treated as direct parameters. To define a parameter as indirect, use the indirect argument when instantiating the Parametrized class.

from typing import Annotated
from pytest_parametrization_annotation import Parametrized


def test(a: Annotated[int, Parametrized(indirect=True)]) -> None:
    ...

In addition, the Parametrized class provides to methods to define default values, default and default_factory.

from typing import Annotated
from pytest_parametrization_annotation import Parametrized


def test(
    a: Annotated[int, Parametrized(default=1)],
    b: Annotated[str, Parametrized(default_factory=lambda: "Hello World!")]
) -> None:
    ...

Defining test cases

To define test cases, use the case marker. Each parametrized argument must be reflected as keyword in the case marker.

from typing import Annotated
import pytest
from pytest_parametrization_annotation import Parametrized


@pytest.mark.case(a=1)
def test(a: Annotated[int, Parametrized]) -> None:
    ...

If a parametrized argument is missing from the case marker, the test suit will fail and a detailed error message will be provided.

examples/test_basic.py::test_addition | Case number 1: Failed to populate because the parameter 'b' is not provided and default is not configured.

Every case marker defines a single test case. To define multiple test cases, use multiple case markers.

from typing import Annotated
import pytest
from pytest_parametrization_annotation import Parametrized


@pytest.mark.case(a=1)
@pytest.mark.case(a=2)
@pytest.mark.case(a=3)
def test(a: Annotated[int, Parametrized]) -> None:
    ...

Optionally a case can be named by providing the first positional argument to the case marker. If this is not provided the default pytest naming scheme is used.

from typing import Annotated
import pytest
from pytest_parametrization_annotation import Parametrized


@pytest.mark.case("Example", a=1)
def test(a: Annotated[int, Parametrized]) -> None:
    ...

Develop

Install dependencies

poetry install

Format code

poetry run ruff format
poetry run black .

Run static type checker

poetry run mypy .

Run tests

poetry run tox run-parallel