Parametrizing tests

pytest · Essentials · 10 min

Table-driven tests cover edge cases cheaply and show exactly which input failed.

@pytest.mark.parametrize("argnames", argvalues) runs a test once per set of values. Each run is a separate test with its own pass or fail and its own node id, like test_slugify[Hello World-hello-world].

  • Several arguments: @pytest.mark.parametrize("text, expected", [("Hello", "hello"), ("A B", "a-b")]).
  • Readable ids: ids=["simple", "space"], or wrap a case with pytest.param(..., id="unicode").
  • Per-case marks: pytest.param("x", marks=pytest.mark.xfail(reason="bug #12")).
  • Stacking two parametrize decorators produces the cartesian product of their values.

Parametrize is ideal when the logic of the test is identical and only the data changes. When cases need different setup or assertions, write separate tests instead of adding if statements inside one.

Choose cases deliberately: typical values, boundaries (empty, zero, one, maximum), and invalid input. A dozen well-chosen cases beat a hundred random ones.

Parametrized values are created at collection time, when pytest imports the module. Expensive objects belong in fixtures, not in parametrize lists. Also avoid mutable objects shared between cases, since the same object is passed to every run that lists it.

Example

# test_slugify.py
import re
import pytest

def slugify(text):
    return re.sub(r"[^a-z0-9]+", "-", text.lower()).strip("-")

@pytest.mark.parametrize("text, expected", [
    ("Hello World", "hello-world"),
    ("  trim  ", "trim"),
    ("C++ & Python!", "c-python"),
    pytest.param("", "", id="empty"),
])
def test_slugify(text, expected):
    assert slugify(text) == expected

@pytest.mark.parametrize("a", [1, 2])
@pytest.mark.parametrize("b", [10, 20])
def test_product(a, b):              # 4 tests: cartesian product
    assert a * b > 0

# $ pytest -q   ->  8 passed

Remember

  • parametrize("a, b", [(1, 2), ...]) runs one test per tuple
  • Each case is a separate test with its own id
  • pytest.param adds ids and marks to a single case
  • Stacked parametrize decorators multiply cases

Watch out for

  • Branching inside a parametrized test because cases need different assertions.

Check yourself

  1. Two stacked parametrize decorators with 3 and 4 values create how many tests?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: docs.pytest.org/en/stable/how-to/parametrize.html