Parametrization
@pytest.mark.parametrize runs one test function against many input/output pairs. One test definition, clear failure output per case.
Search across all documentation pages
@pytest.mark.parametrize runs one test function against many input/output pairs. One test definition, clear failure output per case.
import pytest
@pytest.mark.parametrize("input,expected", [
(2, 4),
(3, 9),
(0, 0),
])
def test_square(input, expected):
assert square(input) == expectedWhen to reach for this:
import pytest
from myapp.billing import apply_discount
from decimal import Decimal
@pytest.mark.parametrize("price,pct,expected", [
(Decimal("100"), 10, Decimal("90")),
(Decimal("100"), 0, Decimal("100")),
(Decimal("50"), 50, Decimal("25")),
(Decimal("0"), 10, Decimal("0")),
])
def test_apply_discount(price, pct, expected):
assert apply_discount(price, pct) == expected
@pytest.mark.parametrize("pct", [-1, 101, 150])
def test_invalid_discount_raises(pct):
with pytest.raises(DiscountError):
apply_discount(Decimal("100"), pct)uv run pytest tests/test_billing.py -v
# test_apply_discount[100-10-90] PASSED
# test_apply_discount[100-0-100] PASSED
# test_invalid_discount_raises[-1] PASSEDWhat this demonstrates:
@pytest.mark.parametrize("pct", [10, 20], ids=["ten-percent", "twenty-percent"])
def test_discount(pct): ...@pytest.mark.parametrize("x", [1, 2])
@pytest.mark.parametrize("y", [10, 20])
def test_combo(x, y): ... # 4 test runs# Parametrize with pytest.param for marks
@pytest.mark.parametrize("n", [
pytest.param(0, marks=pytest.mark.xfail(reason="zero edge case")),
1, 2, 3,
])ids= or use readable values.| Alternative | Use When | Don't Use When |
|---|---|---|
| Hypothesis | Large input spaces | 3-5 known cases |
| Loop in test | Never | - |
| Separate test functions | Unrelated scenarios | Same logic, different inputs |
If the table is hard to read (>15 rows), split or use a CSV fixture.
Yes: @pytest.fixture(params=[...]).
pytest.param(value, marks=pytest.mark.skip(reason="...")).
ids=["case1", "case2"] or ids=lambda val: f"pct-{val}".
Yes. Read a CSV/YAML in conftest.py and pass to parametrize programmatically.
parametrize on test: explicit inputs. Fixture params: shared setup variations.
Put @pytest.mark.parametrize on the class or each method.
No for correctness. Order affects test report sequence only.
Yes with pytest-asyncio. Same decorator on async def test_....
pytest --collect-only lists every parametrized variant.
Stack versions: This page was written for Python 3.14.0, FastAPI 0.115+, Django 5.2, Flask 3.1, Pydantic 2, PyTorch 2.6+, pandas 2.2+, Polars 1.x, ruff 0.9+, and uv 0.6+.
Reviewed by Chris St. John·Last updated Jul 19, 2026