Package: pytest-markdown-docs
Pytest Markdown DocsA plugin forpytestthat uses markdown code snippets from markdown files and docstrings as tests.Detects Python code fences (triple backtick escaped blocks) in markdown files as
well as inline Python docstrings (similar to doctests) and runs them as tests.Python file example:# mymodule.pyclassFoo:defbar(self):"""Bar the fooThis is a sample docstring for the bar methodUsage:```pythonimport mymoduleresult = mymodule.Foo().bar()assert result == "hello"```"""return"hello"Markdown file examples:# TitleLorem ipsum yada yada yada```pythonimportmymoduleresult=mymodule.Foo().bar()assertresult=="hello"```UsageFirst, make sure to install the plugin:pipinstallpytest-markdown-docsTo enable markdown python tests, pass the--markdown-docsflag topytest:pytest--markdown-docsYou can also use themarkdown-docsflag to filteronlymarkdown-docs tests:pytest--markdown-docs-mmarkdown-docsDetection conditionsFence blocks (```) starting with thepython,python3orpylanguage definitions are detected as tests in:Python (.py) files, within docstrings of classes and functions.md,.mdxand.svxfilesSkipping testsTo exclude a Python code fence from testing, add anotestinfo string to the
code fence, e.g:```pythonnotestprint("this will not be run")```Code block dependenciesSometimes you might wish to run code blocks that depend on entities to already
be declared in the scope of the code, without explicitly declaring them. There
are currently two ways you can do this with pytest-markdown:Injecting global/local variablesIf you have some common imports or other common variables that you want to make
use of in snippets, you can add them by creating apytest_markdown_docs_globalshook in yourconftest.py:defpytest_markdown_docs_globals():importmathreturn{"math":math,"myvar":"hello"}With this conftest, you would be able to run the following markdown snippet as a
test, without causing an error:```pythonprint(myvar,math.pi)```FixturesYou can use bothautouse=Truepytest fixtures in a conftest.py or named fixtures with
your markdown tests. To specify named fixtures, addfixture:<name>markers to the code
fence info string, e.g.,```pythonfixture:capsysprint("hello")captured=capsys.readouterr()assertcaptured.out=="hello\n"```As you can see above, the fixture value will be injected as a global. Forautouse=Truefixtures, the value is only injected as a global if it's explicitly added using afixture:<name>marker.Depending on previous snippetsIf you have multiple snippets following each other and want to keep the side
effects from the previous snippets, you can do so by adding thecontinuationinfo string to your code fence:```pythona="hello"``````pythoncontinuationasserta+" world"=="hello world"```Testing of this pluginYou can test this module itself (sadly not using markdown tests at the moment) using pytest:>poetryrunpytestOr for fun, you can use this plugin to include testing of the validity of snippets in this README.md file:>poetryrunpytest--markdown-docsKnown issuesCode for docstring-inlined test discovery can probably be done better (similar to how doctest does it). Currently, seems to sometimes traverse into Python's standard library which isn't great...Traceback logic is extremely hacky, wouldn't be surprised if the tracebacks look weird sometimesLine numbers are "wrong" for docstring-inlined snippets (since we don't know where in the file the docstring starts)Line numbers are "wrong" for continuation blocks even in pure markdown files (can be worked out with some refactoring)There are probably more appropriate ways to use pytest internal APIs to get more features "for free" - current state of the code is a bit "patch it til' it works".Assertions are not rewritten w/ pretty data structure inspection like they are with regular pytest tests by default