pytest Parametrize

Basic @pytest.mark.parametrize

import pytest # Single parameter @pytest.mark.parametrize("n,expected", [ (1, 1), (5, 120), (10, 3628800), ]) def test_factorial(n, expected): assert factorial(n) == expected # Multiple parameters โ€” tuples @pytest.mark.parametrize("input_str,expected", [ ("hello", "HELLO"), ("World", "WORLD"), ("", ""), ("123abc", "123ABC"), ]) def test_to_upper(input_str, expected): assert input_str.upper() == expected # Single list of values @pytest.mark.parametrize("email", [ "user@example.com", "name+tag@domain.co.uk", "dots.allowed@example.org", ]) def test_valid_email(email): assert is_valid_email(email) is True

ids โ€” Custom Test Names

import pytest # Explicit string IDs @pytest.mark.parametrize("value,expected", [ (0, True), (1, False), (-1, False), ], ids=["zero", "positive", "negative"]) def test_is_zero(value, expected): assert (value == 0) == expected # pytest.param for per-case metadata @pytest.mark.parametrize("config", [ pytest.param({"debug": True}, id="debug-mode"), pytest.param({"debug": False}, id="prod-mode"), pytest.param({"debug": True, "verbose": True}, id="full-debug"), ]) def test_app_config(config): app = create_app(config) assert app.configured # IDs from callable @pytest.mark.parametrize("obj", [ pytest.param(User(id=1), id="user-1"), pytest.param(Admin(id=2), id="admin-2"), ])

indirect โ€” Parametrize Through Fixtures

import pytest @pytest.fixture def user_factory(request): """Fixture that receives parametrize value via request.param.""" role = request.param return create_user(role=role, name=f"{role}_user") @pytest.mark.parametrize("user_factory", ["admin", "editor", "viewer"], indirect=True) def test_user_dashboard(user_factory): user = user_factory # already created by fixture response = client.get("/dashboard", user=user) assert response.status_code == 200 # Partial indirect โ€” only some params go through fixtures @pytest.fixture def db_conn(request): return connect(request.param) @pytest.mark.parametrize("db_conn,query", [ ("postgres", "SELECT 1"), ("sqlite", "SELECT 1"), ], indirect=["db_conn"]) def test_basic_query(db_conn, query): result = db_conn.execute(query).fetchone() assert result is not None

Matrix Testing โ€” Stacked parametrize

import pytest # Stacking creates cartesian product (2x3 = 6 test cases) @pytest.mark.parametrize("browser", ["chrome", "firefox"]) @pytest.mark.parametrize("resolution", ["1920x1080", "1366x768", "375x812"]) def test_layout(browser, resolution): # Runs 6 times: chrome/1920, chrome/1366, chrome/375, # firefox/1920, firefox/1366, firefox/375 driver = setup_driver(browser, resolution) assert driver.page_renders_correctly() # Combined with marks @pytest.mark.parametrize("status_code,should_retry", [ pytest.param(500, True, id="server-error"), pytest.param(429, True, id="rate-limit"), pytest.param(404, False, id="not-found"), pytest.param(200, False, marks=pytest.mark.skip(reason="skip success"), id="success"), ]) def test_retry_logic(status_code, should_retry): result = handle_response(MockResponse(status_code)) assert result.should_retry == should_retry

Combining parametrize with Fixtures

import pytest @pytest.fixture def api_client(app): return app.test_client() # Use both fixtures AND parametrize in the same test @pytest.mark.parametrize("endpoint,expected_status", [ ("/health", 200), ("/api/v1/users", 200), ("/nonexistent", 404), ("/api/v1/admin", 403), ]) def test_endpoints(api_client, auth_headers, endpoint, expected_status): response = api_client.get(endpoint, headers=auth_headers) assert response.status_code == expected_status # Dynamic parametrize from external data def load_test_cases(): with open("tests/fixtures/cases.json") as f: return [(c["input"], c["expected"]) for c in json.load(f)] @pytest.mark.parametrize("input_data,expected", load_test_cases()) def test_processor(input_data, expected): assert process(input_data) == expected

Marks with parametrize

MarkUsageEffect
pytest.param(..., marks=pytest.mark.skip)Skip a specific caseCase skipped unconditionally
pytest.mark.skipifConditional skipSkip based on condition
pytest.param(..., marks=pytest.mark.xfail)Expected failureTest passes if it fails
pytest.mark.slowCustom markerSelectable with -m slow
@pytest.mark.parametrize("n", [ pytest.param(0, marks=pytest.mark.xfail(raises=ZeroDivisionError)), pytest.param(1), pytest.param(100), ]) def test_divide(n): assert 100 / n > 0