How to Use pytest.mark.parametrize in Python
This article provides a comprehensive overview of test
parametrization using @pytest.mark.parametrize in Python
test suites. You will learn what test parametrization is, how the
decorator works, its core syntax, how to handle multiple parameters, and
the primary benefits of using this approach to streamline your testing
workflow.
What is Test Parametrization?
Test parametrization is a technique where a single test function is executed multiple times with different sets of input data and expected results. Instead of writing separate functions for every edge case or input combination, you define the test logic once and feed it a collection of arguments.
In Python, the pytest framework implements this feature
primarily through the @pytest.mark.parametrize
decorator.
Basic Syntax and Implementation
The @pytest.mark.parametrize decorator takes two primary
arguments: a comma-separated string of parameter names matching the test
function's arguments, and an iterable (usually a list of tuples)
containing the corresponding data sets.
import pytest
def is_even(number):
return number % 2 == 0
@pytest.mark.parametrize("number, expected", [
(2, True),
(3, False),
(0, True),
(-1, False),
])
def test_is_even(number, expected):
assert is_even(number) == expectedDuring execution, pytest unpacks each tuple and injects
the values into test_is_even(number, expected). It runs the
test four separate times, treating each iteration as an independent test
case.
Execution and Reporting
A key advantage of @pytest.mark.parametrize is how it
handles test isolation and reporting:
- Isolated Failures: If one dataset fails,
pytestcontinues running the remaining parameter sets. A single failure does not halt the entire test suite. - Granular Output: Test runners report each parameter
set as a unique test identifier (e.g.,
test_is_even[2-True],test_is_even[3-False]), making it easy to identify which specific input triggered an error.
Customizing Test IDs
By default, pytest generates identifiers based on the
input values. When dealing with complex objects or edge cases, you can
provide custom labels using the ids parameter:
@pytest.mark.parametrize(
"value, expected",
[(10, True), (-5, False)],
ids=["positive_number", "negative_number"]
)
def test_positive(value, expected):
assert (value > 0) == expectedThis improves readability in test logs, especially during continuous integration (CI) builds.
Advanced Usage: Stacking Parametrizations
You can apply multiple @pytest.mark.parametrize
decorators to a single test function. When stacked, pytest
computes the Cartesian product of all combinations:
@pytest.mark.parametrize("x", [1, 2])
@pytest.mark.parametrize("y", [10, 20])
def test_multiply(x, y):
assert (x * y) > 0This configuration executes four distinct tests:
(x=1, y=10), (x=2, y=10),
(x=1, y=20), and (x=2, y=20).
Benefits of Using
@pytest.mark.parametrize
- DRY Code: Eliminates redundant test code and loops within test functions.
- Improved Maintainability: Adding a new test case requires adding a single tuple to a list rather than writing a new function.
- Accurate Metrics: Accurate test counts in CI dashboards because each input set is tracked as an individual test.