Markers: skip, xfail and custom marks

pytest · Practical · 10 min

Markers let one suite run on every platform and in every pipeline stage, from quick checks to slow integration runs.

Markers attach metadata to tests with @pytest.mark.<name>.

Built-in ones:

  • @pytest.mark.skip(reason="...") always skips.
  • @pytest.mark.skipif(sys.platform == "win32", reason="POSIX only") skips on a condition. Inside a test or fixture, pytest.skip("reason") skips imperatively, and pytest.importorskip("numpy") skips when an optional dependency is missing.
  • @pytest.mark.xfail(reason="bug #42") marks a known failure: a failure is reported as x (xfailed) and doesn't fail the run; an unexpected pass is X (xpassed). With strict=True an unexpected pass fails the run, so you notice when the bug is fixed.
  • @pytest.mark.usefixtures("db") applies fixtures without needing their values.

Custom markers tag tests by category: @pytest.mark.slow, @pytest.mark.integration. Select with -m: pytest -m "not slow" for a fast local run, pytest -m integration in a later CI stage.

Register custom markers in your configuration (markers = [...]) and enable strict_markers, so a typo like @pytest.mark.slwo is an error instead of a silently unselected test.

Apply a marker to every test in a module with pytestmark = pytest.mark.slow at module level, or to a class by decorating it.

Example

# test_markers.py
import sys
import pytest

@pytest.mark.skipif(sys.platform == "win32", reason="uses POSIX paths")
def test_posix_paths():
    assert "/".join(["a", "b"]) == "a/b"

@pytest.mark.xfail(reason="rounding bug #42", strict=True)
def test_known_bug():
    assert round(2.675, 2) == 2.68     # floats: actually 2.67

@pytest.mark.slow
def test_big_computation():
    assert sum(range(10**6)) == 499999500000

def test_optional_dep():
    yaml = pytest.importorskip("yaml")   # skipped if PyYAML is missing
    assert yaml.safe_load("a: 1") == {"a": 1}

# $ pytest -m "not slow" -q
# 2 passed, 1 deselected, 1 xfailed   (yaml test skips without PyYAML)

Remember

  • skip/skipif for tests that can't run here; importorskip for optional deps
  • xfail for known failures; strict=True flags unexpected passes
  • Custom markers + -m select categories like slow or integration
  • Register markers and enable strict_markers to catch typos

Watch out for

  • Leaving xfail non-strict so a fixed bug goes unnoticed for months.

Check yourself

  1. A test marked xfail(strict=True) passes. What is the result?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: docs.pytest.org/en/stable/how-to/skipping.html