@pytest.mark.parametrize eliminates copy-pasted tests by generating multiple test cases from a single function. Markers let you categorise, skip, and xfail individual tests.
@pytest.mark.parametrize
import pytest
def is_palindrome(word):
return word == word[::-1]
@pytest.mark.parametrize("word,expected", [
("racecar", True),
("hello", False),
("level", True),
("python", False),
("noon", True),
])
def test_palindrome(word, expected):
assert is_palindrome(word) == expectedpytest generates 5 separate test cases — each appears independently in the output:
PASSED test_strings.py::test_palindrome[racecar-True]
PASSED test_strings.py::test_palindrome[hello-False]
PASSED test_strings.py::test_palindrome[level-True]Multiple Parameters
@pytest.mark.parametrize("a,b,expected", [
(1, 2, 3),
(0, 0, 0),
(-1, 1, 0),
(100, 200, 300),
])
def test_add(a, b, expected):
assert a + b == expectedParametrize Visualiser
Parametrize test generation
Ctrl+Enter
HTML
CSS
JS
Preview
Built-in Markers
# Skip a test
@pytest.mark.skip(reason="Not implemented yet")
def test_export():
pass
# Skip conditionally
import sys
@pytest.mark.skipif(sys.platform == "win32", reason="Linux only")
def test_file_permissions():
pass
# Expected failure
@pytest.mark.xfail(reason="Known bug #123")
def test_edge_case():
assert my_func(None) == "" # currently raises TypeError
# Slow test — requires --runslow flag
@pytest.mark.slow
def test_full_export():
passCustom Markers
Register custom markers in pytest.ini or pyproject.toml to avoid warnings:
# pytest.ini
[pytest]
markers =
slow: marks tests as slow (deselect with -m "not slow")
integration: marks tests that hit real external services
smoke: marks critical path testsRun subsets:
pytest -m smoke # only smoke tests
pytest -m "not slow" # skip slow tests
pytest -m "integration or smoke"