Marks
The mark is the label the test carries: the @pytest.mark.name decorator (or the module-level pytestmark), and the -m flag selects by the mark (the pytest -m slow runs only the slow, the pytest -m "not slow" the rest). The built-in marks are the skip, the skipif, the xfail, the parametrize, and the filterwarnings; the custom mark (the slow, the network, the integration) is the project's own label, and the markers ini option registers it so the unknown-mark warning does not fire. This chapter covers the built-in marks (the skip, the skipif, the xfail), the custom mark (the markers registration, the -m select, the boolean expression), and the module-level pytestmark.
The Built-in Marks
# the BUILT-IN: the skip, the skipif, the xfail
import pytest, sys
@pytest.mark.skip(reason="not implemented yet")
def test_not_done():
... # the COLLECTED but not RUN (the SKIPPED)
@pytest.mark.skipif(sys.platform == "win32",
reason="unix-only syscall")
def test_unix_only():
... # the condition TRUE -> the SKIPPED,
# the FALSE -> the RUN
@pytest.mark.xfail(reason="known bug #42")
def test_known_bug():
assert word_count("") == 1 # the EXPECTED fail
# the xfail: the FAIL is the XFAIL (the green),
# the PASS is the XPASS (the "unexpected pass")
The built-in marks are the three the suite uses most. The skip is the unconditional (the reason the report's line, the test collected but not run) and the skipif is the conditional (the sys.platform, the version - the environment the test needs). The xfail is the expected-fail: the failing test is the XFAIL (the green, the known bug documented), and the passing one is the XPASS (the bug fixed, the mark stale) - the next chapter details the strict flag that turns the XPASS into the failure. The mark is the label, the -m is the selector, and the custom mark (the next section) extends the same mechanism to the project's own categories (the slow, the network) - the built-in the three behaviors, the custom the N labels.
The Custom Mark and the -m Select
# the CUSTOM mark: the project's label (the slow,
# the network, the integration) - the markers ini
# REGISTERS it (the no unknown-mark warning)
# the pyproject.toml:
# [tool.pytest.ini_options]
# markers = [
# "slow: the long-running (the >5s)",
# "network: the needs the live endpoint",
# "integration: the the real DB, not the mock",
# ]
@pytest.mark.slow
def test_big_corpus():
... # the 20s run (the the CI's the separate job)
@pytest.mark.network
def test_live_api():
... # the the httpbin the the live (the the flaky
# the offline's the the run)
# the SELECT: the -m (the the boolean expression)
pytest -m "slow" # the ONLY the slow
pytest -m "not slow" # the the rest (the the
# the CI's the fast lane)
pytest -m "integration and not network"
pytest -m "slow or network" # the the slow lane
The custom mark is the project's label: the markers ini option registers the name plus the description (the slow: the long-running), which suppresses the unknown-mark warning and documents the label, and the @pytest.mark.slow applies it to the test. The -m flag selects by the mark with the boolean expression (the and/or/not, the parentheses) - the pytest -m "not slow" the CI's fast lane (the unit, the no slow), the pytest -m "slow or network" the slow lane (the separate job, the nightly). The pytestmark at the module level applies the mark to every test in the file (the pytestmark = pytest.mark.integration the whole module the integration) - the file that is all one category, the per-test mark the mixed file. The rule is the one: the mark is the label the -m selects, the markers ini the registration, and the -k (the name) plus the -m (the label) are the two selectors the suite's runs combine - the next chapter's skipif/xfail detail the built-in behavior the mark carries.
The Working Rules
- The
skipis the unconditional, theskipifthe conditional. Thereasonthe report's line (the why, the tracked), theskipifthe environment (thesys.platform, the version) - the test collected but not run, theSKIPPEDthe report's count. - The
xfailis the expected-fail, theXPASSthe stale mark. The fail is the greenXFAIL(the known bug documented), the pass is theXPASS(the bug fixed, the mark to remove) - thestrictflag the next chapter turns theXPASSinto the failure. - The
markersini registers the custom mark. Themarkers = ["slow: ...", "network: ..."]the no unknown-warning plus the doc, the@pytest.mark.slowthe apply, and thepytestmarkthe module-level apply to the whole file. - The
-mis the label selector, the-kthe name selector. The-m "not slow"the fast lane, the-m "slow or network"the slow lane (the separate job), and the-kplus the-mcombine (the name AND the label) - the two selectors the suite's runs use.
Summary
The mark is the label the test carries (the @pytest.mark.name, the module-level pytestmark), and the -m flag selects by the mark with the boolean expression (the and/or/not). The built-in marks are the skip (the unconditional, the reason the report's line), the skipif (the conditional, the sys.platform the environment), and the xfail (the expected-fail, the XFAIL the green, the XPASS the stale) - plus the parametrize and the filterwarnings. The custom mark (the slow, the network, the integration) is the project's label, the markers ini option the registration (the no unknown-warning, the doc), and the -m "not slow" the CI's fast lane versus the -m "slow or network" the slow lane. The pytestmark applies the mark to the whole module, and the -k (the name) plus the -m (the label) are the two selectors the runs combine. The next chapter details the skipif and the xfail behavior (the strict, the run) the mark carries.