Quick-start reference

pytest-codeblock collects Python code blocks from .rst and .md files and runs them as pytest tests. No configuration is required for basic use — files are discovered automatically.


Naming rules

Only blocks whose name starts with test_ are collected by default. To test all blocks regardless of name, set test_nameless_codeblocks = true in pyproject.toml.


RST syntax

.. code-block:: python
   :name: test_my_example

   result = 1 + 1
   assert result == 2

Add a pytest marker (RST)

.. pytestmark: skip
.. code-block:: python
   :name: test_skipped_block

   pass

Request a fixture (RST)

.. pytestfixture: tmp_path
.. code-block:: python
   :name: test_uses_tmp_path

   d = tmp_path / "sub"
   d.mkdir()
   assert d.is_dir()

Group blocks (RST) — shared context

.. code-block:: python
   :name: test_part_one

   x = 1

Some prose in between.

.. continue: test_part_one
.. code-block:: python
   :name: test_part_two

   y = x + 1
   assert y == 2

All blocks sharing the same group key are concatenated into one test under the first name.

Incremental grouping — each step is its own test

When every continuation block has a distinct name, each step becomes a cumulative test:

.. code-block:: python
   :name: test_step_1

   a = 1

.. continue: test_step_1
.. code-block:: python
   :name: test_step_2

   b = a + 1
   assert b == 2

This produces two tests: test_step_1 (code: a=1) and test_step_2 (code: a=1\nb=a+1\nassert b==2).

Literal block (RST)

.. codeblock-name: test_literal_block

Example code::

   result = "hello"
   assert result == "hello"

Include external file (RST)

.. literalinclude:: examples/snippet.py
   :name: test_external_snippet

Run as standalone pytest suite (RST)

.. pytestmark: pytestrun
.. code-block:: python
   :name: test_pytest_style

   import pytest

   class TestMath:
       def test_add(self):
           assert 1 + 1 == 2

Markdown syntax

```python name=test_my_example
result = 1 + 1
assert result == 2
```

Add a pytest marker (Markdown)

<!-- pytestmark: skip -->
```python name=test_skipped
pass
```

Request a fixture (Markdown)

<!-- pytestfixture: tmp_path -->
```python name=test_uses_tmp_path
d = tmp_path / "sub"
d.mkdir()
assert d.is_dir()
```

Group blocks (Markdown)

```python name=test_setup
x = 1
```

<!-- continue: test_setup -->
```python name=test_continuation
y = x + 1
assert y == 2
```

Run as standalone pytest suite (Markdown)

<!-- pytestmark: pytestrun -->
```python name=test_class_example
import pytest

class TestMath:
    @pytest.fixture
    def value(self):
        return 42

    def test_value(self, value):
        assert value == 42
```

Async support

Top-level await is automatically wrapped — no extra config needed:

.. code-block:: python
   :name: test_async_block

   import asyncio
   result = await asyncio.sleep(0.1, result=99)
   assert result == 99

pyproject.toml configuration

[tool.pytest-codeblock]
# Test all blocks regardless of test_ prefix (default: false)
test_nameless_codeblocks = false

# Add custom language identifiers (in addition to python, py, python3)
rst_user_codeblocks = []
md_user_codeblocks = []

# Add custom file extensions
rst_user_extensions = []
md_user_extensions = []

testpaths troubleshooting

If docs are not discovered, add explicitly:

[tool.pytest.ini_options]
testpaths = ["src/app/tests", "docs"]

conftest.py hook integration

Use CODEBLOCK_MARK from pytest_codeblock.constants to identify doc-block tests:

from pytest_codeblock.constants import CODEBLOCK_MARK

def pytest_collection_modifyitems(config, items):
    for item in items:
        if item.get_closest_marker(CODEBLOCK_MARK):
            item.add_marker(pytest.mark.documentation)

Custom fixtures used in doc blocks are defined in conftest.py exactly like regular fixtures. Multiple pytestfixture directives on consecutive lines are all applied to the next block. Fixture requests in the first block of a group automatically apply to all continuation blocks.