The HTML comment placed above (or around) a Python code block is the only
configuration interface for markdown-pytest. This page documents every
parameter and every valid syntax variant.
Marks a block as a subtest. The first block with a given name and no
case: key is the shared setup; each case: block is an independent
subtest that runs after the setup.
<!-- name: test_foo; case: my_scenario -->
Case names can contain spaces: case:handlesemptyinput.
If all blocks have case:, there is no shared setup — each case
runs in its own fresh namespace.
A code block placed inside the comment is hidden from rendered
Markdown but executed as part of the test. The metadata keys come first,
then the code fence:
The closing `-->` appears **after** the closing ` ``` `. The opening
` ```python ` and closing ` ``` ` are part of the comment.
## Full example
A test with all parameters used together:
```{code-block} text
<!--
name: async test_full_example;
fixtures: tmp_path, monkeypatch;
mark: xfail(strict=False, reason="experimental")
```python
# hidden setup block
import json
path = tmp_path / "data.json"
```
-->
```python
# visible block — readers see this
path.write_text(json.dumps({"key": "value"}))
## Ignored blocks
A Python block is **silently ignored** when:
- It has no comment above (or around) it.
- The comment has no `name:` key.
- The `name:` value does not start with the configured prefix.
- The block uses a language tag other than `python`.
- The block uses a non-triple backtick fence.
Comment syntax reference¶
The HTML comment placed above (or around) a Python code block is the only configuration interface for markdown-pytest. This page documents every parameter and every valid syntax variant.
Basic structure¶
Key-value pairs are separated by semicolons (
;).The trailing semicolon after the last pair is optional.
Whitespace around keys and values is stripped.
Comments can span multiple lines.
Parameters¶
name(required)¶The test identifier. Must be unique per file (within a given name, all blocks are merged into one test).
Rules:
Must start with the configured prefix (
testby default — see Configuration reference).Must be a valid Python identifier: letters, digits, and underscores. No spaces (spaces before the prefix
asyncare the only exception).Case-sensitive:
test_fooandtest_Fooare two different tests.Async prefix:
Prefix the name with
asyncto enable top-levelawait:The
asyncpart is stripped from the collected test name — the test is namedtest_my_async_featurein pytest output.case¶Marks a block as a subtest. The first block with a given name and no
case:key is the shared setup; eachcase:block is an independent subtest that runs after the setup.Case names can contain spaces:
case: handles empty input.If all blocks have
case:, there is no shared setup — each case runs in its own fresh namespace.Subtests require pytest 9.0+.
fixtures¶Comma-separated list of pytest fixture names to inject as variables.
The fixture values are available as variables with the same names inside the code block.
Multiline form:
<!-- name: test_foo; fixtures: tmp_path, monkeypatch, capsys -->Multiple
fixtures:keys:<!-- name: test_foo; fixtures: tmp_path; fixtures: monkeypatch -->Duplicate keys are merged — the two forms above are equivalent.
Only the first block needs
fixtures:when a test is split across multiple blocks. Later blocks share the same namespace.subprocess¶Set to
trueto run the test in a separate Python process.Fixtures and
case:subtests are not available in subprocess mode.Async code is wrapped in
asyncio.run()automatically.The child process inherits the parent’s
sys.path.A non-zero exit code fails the test.
repl¶Set to
trueto run the block as a doctest (interactive shell session).Lines starting with
>>>are executed.Lines starting with
...are continuation lines.Following lines without a prompt are expected output.
All standard
# doctest: +DIRECTIVEflags are supported.Cannot be combined with
subprocess: true.mark¶A pytest mark expression, evaluated as
pytest.mark.<expression>.Multiple
mark:keys are merged — all marks are applied:<!-- name: test_multi; mark: xfail; mark: slow -->Comment delimiters¶
Both two-dash and three-dash variants are recognised. All four forms are equivalent:
Hidden block syntax¶
A code block placed inside the comment is hidden from rendered Markdown but executed as part of the test. The metadata keys come first, then the code fence:
–>
The closing `-->` appears **after** the closing ` ``` `. The opening ` ```python ` and closing ` ``` ` are part of the comment. ## Full example A test with all parameters used together: ```{code-block} text <!-- name: async test_full_example; fixtures: tmp_path, monkeypatch; mark: xfail(strict=False, reason="experimental") ```python # hidden setup block import json path = tmp_path / "data.json" ``` --> ```python # visible block — readers see this path.write_text(json.dumps({"key": "value"}))