Doctest Support

Purpose

Doctest Support addresses the need to seamlessly collect and run doctests embedded in Python modules and standalone text files within the pytest framework. Doctests are interactive Python examples embedded in docstrings or text files that serve both as documentation and as simple tests. This subtopic enables pytest users to leverage doctests alongside other test styles, ensuring consistent test discovery, execution, and reporting without requiring separate tooling.

Functionality

The core functionality revolves around discovering doctests in `.py` modules and `.txt` or `.rst` files (matching configurable patterns), executing these doctests with fine-grained control, and integrating their results into pytest's test outcome reporting.

Key Workflows

Configuration and Option Flags

Enhanced Output Checking

Relationship to Parent Topic and Other Subtopics

Doctest Support complements the broader **Integration with Other Test Frameworks** topic by enabling pytest to run doctests natively, alongside unittest-based tests and standard pytest tests. This integration ensures:

This subtopic introduces advanced doctest runner customization and output checking that are not covered in the parent topic or other subtopics, focusing specifically on the unique challenges of doctest execution, error aggregation, and output comparison.

Code Snippet Illustrations

Customized Doctest Runner Class

A specialized runner aggregates failures and supports continuing execution after failures:

class PytestDoctestRunner(doctest.DebugRunner):
    def __init__(self, ..., continue_on_failure=True):
        super().__init__(...)
        self.continue_on_failure = continue_on_failure

    def report_failure(self, out, test, example, got):
        failure = doctest.DocTestFailure(test, example, got)
        if self.continue_on_failure:
            out.append(failure)
        else:
            raise failure

Doctest Item Setup Integrating Fixtures

During setup, the doctest’s globals are updated with values provided by the `doctest_namespace` fixture, enabling fixture support:

def setup(self):
    self._request._fillfixtures()
    globs = dict(getfixture=self._request.getfixturevalue)
    for name, value in self._request.getfixturevalue("doctest_namespace").items():
        globs[name] = value
    self.dtest.globs.update(globs)

Collection Decision Logic

The collector decides whether to collect a file as a doctest module or textfile:

def pytest_collect_file(file_path, parent):
    if file_path.suffix == ".py" and config.option.doctestmodules and not is_setup_or_main_py(file_path):
        return DoctestModule.from_parent(parent, path=file_path)
    elif is_doctest_text_file(config, file_path, parent):
        return DoctestTextfile.from_parent(parent, path=file_path)
    return None

Diagram

flowchart TD
    A[Start: Pytest Test Collection]
    A --> B{File Type}
    B -->|Python Module (.py)| C{--doctest-modules?}
    C -->|Yes| D[Collect DoctestModule]
    C -->|No| E[Skip Doctest Collection]
    B -->|Text File (.txt/.rst)| F{Matches doctest glob?}
    F -->|Yes| G[Collect DoctestTextfile]
    F -->|No| E
    D --> H[Find Doctests in Module]
    G --> I[Parse Doctests in Text]
    H --> J[Create DoctestItems]
    I --> J
    J --> K[Setup DoctestItem: Inject Fixtures]
    K --> L[Run Doctest with Custom Runner]
    L --> M{Failures?}
    M -->|Yes| N[Aggregate & Report Failures]
    M -->|No| O[Pass]
    N --> P[Display Detailed Diff]
    O --> P
    P --> Q[Continue or Stop Based on Config]

This flowchart visualizes the lifecycle of doctest support from collection through execution and failure reporting, highlighting key decision points and integration with fixtures and configuration.


Doctest Support enriches pytest’s versatility by enabling embedded documentation tests to participate fully in the testing workflow, benefiting from pytest’s powerful fixture system, reporting, and extensibility.