How to use pytest marks

pytest marks let you attach metadata to tests — skip them, mark them as expected failures, or tag them for selective runs. markdown-pytest supports the full marks system via the mark: key in the HTML comment.

Basic syntax

<!-- name: test_name; mark: <expression> -->

The expression is evaluated as pytest.mark.<expression>. Everything after mark: is passed directly to pytest’s mark machinery.

Skip a test

<!-- name: test_needs_network; mark: skip(reason="requires network access") -->

The test is collected but skipped. It appears as s in the summary:

guide.md::test_needs_network SKIPPED (requires network access)

Conditional skip

Use skipif to skip based on a runtime condition:

<!-- name: test_unix_only; mark: skipif(sys.platform == "win32", reason="POSIX only") -->

Note: the expression is evaluated at collection time, so sys must be importable without additional setup (it always is).

Mark a test as expected to fail

xfail marks a test that is known to fail. It passes (as x) if it fails, and surprises (as X) if it unexpectedly passes.

<!-- name: test_known_bug; mark: xfail(reason="issue #42, not fixed yet") -->

Expected failure with a specific exception

<!-- name: test_divide_by_zero; mark: xfail(raises=ZeroDivisionError) -->
```python
1 / 0

If the block raises `ZeroDivisionError`, the test is marked `xfail` (as
expected). If it raises a different exception, the test fails.

### Strict xfail

`strict=True` turns an unexpected pass into a test failure:

```{code-block} markdown
<!-- name: test_strict_xfail; mark: xfail(strict=True, reason="must fail") -->

Use this when a test must fail — an unexpected pass means the bug was fixed and the mark should be removed.

Apply multiple marks

Use semicolons to add more metadata keys, but each test supports only one mark: value. To apply multiple marks, use comma-separated mark. calls in a single expression — or use a custom mark:

<!-- name: test_slow_network; mark: xfail(reason="slow"); mark: skip(reason="no network") -->

When mark: appears multiple times, the values are merged — both marks are applied.

Marks with split blocks

Only the first block needs the mark: declaration. Subsequent blocks with the same name inherit the mark:

<!-- name: test_marked_split; mark: xfail -->
```python
x = 1
assert x == 2   # fails — expected

## Custom marks

Register your custom marks in `pyproject.toml`:

```{code-block} toml
[tool.pytest.ini_options]
markers = [
    "slow: marks tests as slow (deselect with '-m \"not slow\"')",
    "integration: requires external services",
]

Then use them in Markdown:

<!-- name: test_heavy; mark: slow -->
```python
import time
time.sleep(0.001)
assert True

Run only fast tests:

```{code-block} bash
pytest guide.md -m "not slow"

Selecting tests by mark

All standard -m expressions work:

pytest guide.md -m "not slow"
pytest guide.md -m "integration or smoke"
pytest guide.md -m "xfail"

Reference

See the full list of built-in marks in the pytest marks documentation.