View on GitHub

pytest-phmdoctest

Python syntax highlighted Markdown doctest pytest plugin.

pytest-phmdoctest 1.0.0

Introduction

Python syntax highlighted Markdown doctest pytest plugin.

A pytest plugin based on the phmdoctest command line tool.

If you have Python syntax highlighted examples in Markdown like this Python code…

print("Hello World!")

plus expected output.

Hello World!

This pytest plugin will test them, as is, without edits. On this file try the command …

pytest -v --phmdoctest README.md

pytest console output …

plugins: phmdoctest-1.0.0
collected 1 item

::README.py::test_code_12_output_16 PASSED

The plugin also tests Python interactive sessions described by doctest. See testing examples below.

The 12 in the function name test_code_12_output_16 is the line number of the first line of python code. 16 shows the line number of the expected terminal output.

Description

The plugin is based on the Python tool phmdoctest version >= 1.4.0 It generates a pytest test file from a Markdown file.

main branch status

Code style: black

CI Test Build status readthedocs codecov

Website | Docs | Repos | pytest | Codecov | License

Introduction | Description | Installation | Usage | Testing Python interactive sessions | Generate test files | Generate and collect test files | Help | Configure collection | Hints | Related projects

Changes | Contributions

Installation

It is advisable to install in a virtual environment.

python -m pip install pytest-phmdoctest

Usage

Consider a project with the following files. Not all files are shown.

README.md
doc/directive2.md
doc/nocode.md
tests/test_example.py
pytest -v
plugins: phmdoctest-1.0.0
collected 1 item

tests/test_example.py::test_example PASSED

Use --phmdoctest to also collect Markdown files.

pytest -v --phmdoctest
plugins: phmdoctest-1.0.0
collected 6 items

::README.py::test_code_10_output_17 PASSED
::doc__directive2.py::test_code_25_output_32 PASSED
::doc__directive2.py::test_code_42_output_47 PASSED
::doc__directive2.py::test_code_52_output_56 PASSED
::doc__project.py::test_code_12_output_19 PASSED
tests/test_example.py::test_example PASSED

Use --phmdoctest in a pytest ini file instead of on the command line as shown below:

# pytest.ini and tox.ini
[pytest]
addopts = --phmdoctest
# pyproject.toml
[tool.pytest.ini_options]
addopts = "--phmdoctest"
# setup.cfg  Note: Use is discouraged by pytest docs.
[tool:pytest]
addopts = --phmdoctest

Testing Python interactive sessions

The plugin also tests Python interactive sessions described by doctest like this one:

>>> import math
>>> math.floor(9.1)
9

Use the --phmdoctest-docmod option to collect both Python code/expected output and Python interactive sessions.

pytest -v --phmdoctest-docmod
plugins: phmdoctest-1.0.0
collected 10 items

::README.py::README.session_00001_line_24 PASSED
::README.py::test_code_10_output_17 PASSED
::doc__directive2.py::test_code_25_output_32 PASSED
::doc__directive2.py::test_code_42_output_47 PASSED
::doc__directive2.py::test_code_52_output_56 PASSED
::doc__project.py::doc__project.session_00001_line_31 PASSED
::doc__project.py::doc__project.session_00002_line_46 PASSED
::doc__project.py::doc__project.session_00003_line_55 PASSED
::doc__project.py::test_code_12_output_19 PASSED
tests/test_example.py::test_example PASSED

Here is simulated output captured when --phmdoctest-docmod doctest collection breaks due to incompatible DoctestModule API.

pytest -v --phmdoctest-docmod
plugins: phmdoctest-1.0.0
collected 8 items

::README.py::test_code_10_output_17 PASSED
::README.py::test_unable_to_collect_doctests FAILED
::doc__directive2.py::test_code_25_output_32 PASSED
::doc__directive2.py::test_code_42_output_47 PASSED
::doc__directive2.py::test_code_52_output_56 PASSED
::doc__project.py::test_code_12_output_19 PASSED
::doc__project.py::test_unable_to_collect_doctests FAILED
tests/test_example.py::test_example PASSED

Generate test files

Save generated test files to the file system. Do not collect them. The plugin does not use the non-public DoctestModule when doing this.

pytest -v --phmdoctest-generate .gendir
plugins: phmdoctest-1.0.0
collected 1 item

tests/test_example.py::test_example PASSED
test_doc__directive2.py
test_doc__project.py
test_README.py

Generate and collect test files

A single pytest command will generate test files and collect them. The plugin does not use the non-public DoctestModule when doing this.

pytest -v --phmdoctest-generate .gendir . .gendir --doctest-modules --ignore src
plugins: phmdoctest-1.0.0
collected 10 items

tests/test_example.py::test_example PASSED
.gendir/test_README.py::test_README.session_00001_line_24 PASSED
.gendir/test_README.py::test_code_10_output_17 PASSED
.gendir/test_doc__directive2.py::test_code_25_output_32 PASSED
.gendir/test_doc__directive2.py::test_code_42_output_47 PASSED
.gendir/test_doc__directive2.py::test_code_52_output_56 PASSED
.gendir/test_doc__project.py::test_doc__project.session_00001_line_31 PASSED
.gendir/test_doc__project.py::test_doc__project.session_00002_line_46 PASSED
.gendir/test_doc__project.py::test_doc__project.session_00003_line_55 PASSED
.gendir/test_doc__project.py::test_code_12_output_19 PASSED

How it works:

These are the ini file settings:

# pytest.ini and tox.ini
[pytest]
addopts = --phmdoctest-generate=.gendir --doctest-modules --ignore src
# pyproject.toml
[tool.pytest.ini_options]
addopts = "--phmdoctest-generate=.gendir --doctest-modules --ignore src"
# setup.cfg  Note: Use is discouraged by pytest docs.
[tool:pytest]
addopts = --phmdoctest-generate=.gendir --doctest-modules --ignore src

Here is a demo that runs on a checked out copy of the repository.

# With a terminal in the tests/sample directory
# The first line collects 5 items.
# The second line collects 3 items.

pytest -v --phmdoctest-generate=.gendir "." .gendir --ignore README.md --ignore doc/directive2.md --doctest-modules --ignore src
pytest -v --phmdoctest-generate=.gendir "." .gendir  --ignore-glob */*.md --doctest-modules --ignore src

Help

pytest --help contains a phmdoctest: group in the middle and an ini-option near the bottom. The help contains:

Configure collection

An optional phmdoctest-collect = section can be placed in the pytest ini file. It is a list of lines of the format

glob [options]

Consider using the section to pass collection options described below. The options have the same names and behave like the options accepted by phmdoctest usage.

Example

# pytest.ini
[pytest]
addopts = --phmdoctest-docmod
phmdoctest-collect =
    doc/project.md --skip greeting --skip enjoyment
    **/*.md

Then run this pytest command on the project files from the Usage section …

pytest -v --ignore tests/test_example.py

output

plugins: phmdoctest-1.0.0
collected 7 items

::README.py::README.session_00001_line_24 PASSED
::README.py::test_code_10_output_17 PASSED
::doc__directive2.py::test_code_25_output_32 PASSED
::doc__directive2.py::test_code_42_output_47 PASSED
::doc__directive2.py::test_code_52_output_56 PASSED
::doc__project.py::doc__project.session_00001_line_31 PASSED
::doc__project.py::doc__project.session_00002_line_46 PASSED

phmdoctest-collect options

-s, --skip TEXT

Do not test blocks with substring TEXT. Allowed multiple times.

--fail-nocode

Markdown file with no code blocks left after applying skips generates a failing test.

-u, --setup TEXT

Run block with substring TEXT at test module setup time.

-d, --teardown TEXT

Run block with substring TEXT at test module teardown time.

--setup-doctest

Make globals created by the --setup Python code block or setup directive visible to Python interactive session »> blocks. Caution: The globals are set at pytest Session scope. The globals are visible to all doctests in the test suite. This includes doctests collected by the plugin and doctests collected from other files due to --doctest-modules.

Notes

Equivalent directives

The HTML directive comments below placed in the Markdown file can be used instead of specifying options in the phmdoctest-collect section. Directives select a single fenced code block. There are 10 directives in phmdoctest Directives. Here are the directives equivalent to the collect section options.

  collect-section          HTML Directive
  option                   comment
------------------------------------------------
-s, --skip TEXT        <!--phmdoctest-skip-->
-u, --setup TEXT       <!--phmdoctest-setup-->
-d, --teardown TEXT    <!--phmdoctest-teardown-->

Hints