How These Docs Are Tested¶
Every console transcript on this site is a test. If the library's behaviour changes, the affected page fails in CI — documentation cannot silently drift.
The mechanism¶
Pages are collected by pytest as doctests:
[tool.pytest.ini_options]
addopts = "--doctest-glob=*.md --doctest-continue-on-failure"
testpaths = ["tests", "docs"]
doctest_optionflags = ["NORMALIZE_WHITESPACE", "ELLIPSIS", "IGNORE_EXCEPTION_DETAIL"]
A block written like this:
```pycon
>>> class EventSchema(ModelSchema):
... class Config:
... model = models.Event
... fields = ['title']
>>> EventSchema.model_validate(models.Event(title='DjangoCon')).model_dump()
{'title': 'DjangoCon'}
```
…is executed, and the line after the prompt is compared against what the library actually returned.
What the examples run against¶
docs/conftest.py gives every page a database and a preloaded namespace, so the
pages stay readable without a setup block at the top of each one:
| Name | Value |
|---|---|
models |
examples/models.py — the models shown in the guides |
ModelSchema, Schema, SchemaFactory |
The public schema classes |
Source, MethodSource, SourceResolver, SourceResolutionError |
Source metadata API |
Annotated, json, uuid4 |
Standard-library helpers |
The database is a real (in-memory) SQLite database provided by
pytest-django, rolled back between pages. Examples that call
objects.create() are doing real ORM work.
Running them¶
A failure prints the expected and actual output side by side, pointing at the line in the Markdown file:
File "docs/guides/source.md", line 42, in source.md
Failed example:
EventSchema.model_validate(event).model_dump()
Expected:
{'title': 'DjangoCon', 'category_name': 'Django'}
Got:
{'title': 'DjangoCon', 'category_name': 'Python'}
Writing a new example¶
- Use a
```pyconfence and>>>prompts. - Leave a blank line before the closing fence. doctest reads expected
output until a blank line; without one it tries to match the
```too. - Use names from the table above rather than adding imports, unless the import is itself the point of the example.
- Prefer showing a real value over asserting
True.model_dump()output is more useful to a reader thanassert x == y. - Run the page. Do not hand-write the expected output — paste what the library produced, once you have confirmed it is correct.
For long output, print(json.dumps(..., indent=2)) reads better than a raw
dict, and NORMALIZE_WHITESPACE keeps it robust.
Exceptions¶
Show the traceback in doctest form:
```pycon
>>> Source('')
Traceback (most recent call last):
...
ValueError: Source path cannot be empty.
```
When only part of a long message matters, end it with ... — ELLIPSIS is
enabled.
Snippets from real files¶
Model definitions are included from examples/models.py with
pymdownx.snippets,
so the models shown are the models the examples import:
```python title="models.py"
class Event(models.Model):
title = models.CharField(max_length=100)
category = models.OneToOneField(Category, null=True, on_delete=models.SET_NULL)
class Meta:
app_label = 'examples'
def display_title(self) -> str:
return f'Event: {self.title}'
```
The markers live in the Python file:
mkdocs build --strict fails if a referenced snippet or marker is missing.
In CI¶
The documentation build and the doctests both run on every push. A page whose output no longer matches fails the build in the same way a unit test does.
Related pages¶
- Publishing Documentation — how the site is deployed
- Credits and Stewardship