Using pytest.mark.parametrize to Generate Multiple Test Cases
Learn how @pytest.mark.parametrize creates multiple test invocations from a single function, with a concrete example, mechanism explanation, limits, and verification steps.
16 Sept 2026, 16:38 UTC

Useful answer
The @pytest.mark.parametrize decorator lets you write a single test function that pytest will call repeatedly with different argument sets. Each set becomes a distinct test item, making it easy to cover many input‑output combinations without duplicating code.
Worked example
Suppose you want to verify a simple addition function for several pairs of numbers. The following code defines three test cases in one function:
import pytest
def add(x, y):
return x + y
@pytest.mark.parametrize('a,b,expected', [
(1, 2, 3),
(2, 3, 5),
(0, 0, 0),
])
def test_addition(a, b, expected):
assert add(a, b) == expected
When pytest collects tests, it expands test_addition into three separate items:
- test_addition[1-2-3]
- test_addition[2-3-5]
- test_addition[0-0-0]
Each item receives its own node ID, runs independently, and reports its own pass/fail status.
Mechanism
During test collection, pytest reads the parametrize marker, creates a Cartesian product of the supplied argument tuples, and generates a new test function for each combination. The generated tests inherit any fixtures declared in the original function, unless you explicitly change the fixture scope.
Limits and common mistakes
Test suite explosion
Adding multiple parametrize markers multiplies the number of generated tests. For example, two markers with 5 and 10 values produce 50 test cases. Large combinatorial expansions can slow CI pipelines and make debugging harder. A practical check is to run pytest --collect-only and count the listed items; if the number grows unexpectedly, consider reducing the parameter sets or using a data‑driven approach outside of pytest.
Parameter‑signature mismatches
The function parameters must exactly match the names given in the marker, in the same order. Forgetting a parameter or swapping two names leads to fixture lookup errors or silent misuse of values. To verify, run the test with -v and ensure each test ID shows the expected argument values in the output.
Non‑hashable values
If a parameter set contains a mutable object like a list or dict, pytest raises a ValueError during collection because it cannot hash the value for internal indexing. Convert such objects to tuples or use ids to provide a string identifier:
@pytest.mark.parametrize('data', [
([1, 2, 3], 'list‑1'),
([4, 5], 'list‑2'),
], ids=lambda v: v[1])
def test_data(data, _):
assert len(data[0]) > 0
Fixture scope leakage
By default, parametrized tests share the same fixture scope (usually function). If a fixture modifies state that is not reset between parametrized runs, later tests may see leftovers. To isolate state, either scope the fixture to function (the default) and ensure it cleans up, or explicitly set scope='function' on the fixture and avoid using class or module scopes for mutable resources.
Practical verification
- Run
pytest --collect-onlyand confirm that each parameter set appears as a separate test ID. - Execute the suite with
pytest -vand watch the output: each ID should show its own pass/fail line. - Introduce a deliberate failure in one parameter set (e.g., change the expected sum) and verify that only the corresponding test ID fails while the others pass.
When to avoid parametrize
If the test logic varies significantly between cases (different setup, different assertions), it may be clearer to write separate test functions or use a test generator pattern. Parametrize shines when the test body is identical and only the inputs change.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.