はじめに
あのはな再放送! nikkieです
Sphinx拡張のテストコードを劇的に簡単に書けるpytestのマーカーを知ってしまいました...
目次
自作Sphinx拡張とE2E
SphinxでビルドしたHTML中の、外部ページへのリンクを、ブラウザの別のタブで開くようにする拡張を自作しています。
GitHub - ftnext/sphinx-new-tab-link: Open external links in new tabs of the browser in Sphinx HTML documents
PyPIで公開するパッケージにはテストコードを用意することを自分ルールとしており、この拡張にもテストコードを書きました。
テストでもSphinxを動かしてreSTファイルからHTMLをビルドしています。
このことを指してE2Eと書きました(End-to-Endテスト)
PyCon JP 2022のattakeiさんのスライドより
このE2Eを書くのに私はとても苦労した1のですが、なんとSphinxが@pytest.mark.sphinx
という、E2E向け超便利マーカーを提供していました!
@pytest.mark.sphinx
マーカーを知った経緯
ドキュメント「Testing API」
このたびドキュメントを発見しました。
pytestのプラグインとしてconftest.pyに指定します2。
pytest_plugins = ('sphinx.testing.fixtures',)
使用方法はSphinxのテストコードが案内されます。
それでは見ていきましょう
Sphinxのテストコード
pytest_plugins
に指定済みです。
https://github.com/sphinx-doc/sphinx/blob/v8.0.2/tests/conftest.py#L35
具体的なテストコードとして、(関心を引いた)test_api_translator.py
を見ました。
https://github.com/sphinx-doc/sphinx/blob/v8.0.2/tests/test_writers/test_api_translator.py
そこで見つけたのが、今回のマーカーです!
# https://github.com/sphinx-doc/sphinx/blob/v8.0.2/tests/test_writers/test_api_translator.py#L24-L29 @pytest.mark.sphinx('html', testroot='api-set-translator') def test_html_with_set_translator_for_html_(app): # use set_translator() translator_class = app.builder.get_translator_class() assert translator_class assert translator_class.__name__ == 'ConfHTMLTranslator'
testroot
に指定しているのは、roots/test-api-set-translator
ディレクトリ!
https://github.com/sphinx-doc/sphinx/tree/v8.0.2/tests/roots/test-api-set-translator
ディレクトリ名先頭の「test-」は省略できるのだと理解しました。
app
はsphinx.testing.fixtures
が提供するフィクスチャの1つ。
Sphinxアプリケーションを表します。
これをテスト向けの設定で動かすために、@pytest.mark.sphinx
マーカーを指定するのです!
逆に言えば、このマーカーを付けるだけなんです!
@pytest.mark.sphinx
マーカーを使ってテストを書く
テストコードの検証に使った環境
conftest.py
でpytest_plugins
を設定したあとは、マーカーの一覧に表示されます。
% pytest --markers @pytest.mark.sphinx(buildername="html", *, testroot="root", srcdir=None, confoverrides=None, freshenv=False, warningiserror=False, tags=None, verbosity=0, parallel=0, keep_going=False, builddir=None, docutils_conf=None): arguments to initialize the sphinx test application.
テストの実装としては、tests/test-default
ディレクトリを用意して
. └── tests/ ├── roots/ │ └── test-default/ │ ├── conf.py │ └── index.rst ├── __init__.py ├── conftest.py └── test_example.py
test_example.py
に書きました。
import pytest @pytest.mark.sphinx("html", testroot="default") def test_example(app): app.build() assert (app.outdir / "index.html").exists() # この記事では省略しますが、BeautifulSoup4でHTMLをパースして検証できる
pytest -sv tests/test_example.py
で実行すると、test-default
下のreSTファイルからHTMLをビルドしている様子が確認できます
test-default
下のreSTファイルは一時ディレクトリにコピー- 一時ディレクトリのreSTファイル一式を指定して、
make html
相当のビルド実施 - 一時ディレクトリ下にHTMLファイルが生成される(直近数世代を保持するため、開いて確認も可能)
このマーカーについてのドキュメンテーションを私の積ん読リストとして示します。
- マーカーとして追加
- マーカーの引数の処理(
app_params
フィクスチャ) - 上記のテストコードの
app
フィクスチャはapp_params
フィクスチャを呼び出す(のでマーカーの設定値が使われる)
終わりに
Sphinx拡張のE2Eを書く際には@pytest.mark.sphinx
マーカーがとてもよさそうです。
conftest.py
でpytest_plugins
に"sphinx.testing.fixtures"
を指定(フィクスチャやマーカーが有効に)- ビルドするreSTファイルは
roots/test-hamegg
のように配置(最小限のSphinxプロジェクト) @pytest.mark.sphinx("html", testroot="hamegg")
とマーカーを指定すると、app
フィクスチャの返り値はマーカーに渡した値で設定されたSphinxアプリケーションpytest -s
と実行すると、テストで生成されたHTMLの保存場所を確認できる
これまで私が書いていたE2Eのコードのほとんどは、このマーカーを使うだけで書かなくてよくなります。
今回は知った感動で書き上げましたが、次は互換かどうかを確認して乗り換えたいと思います
- pytestのフィクスチャを理解できたのでいい思い出ではあります ↩
- https://docs.pytest.org/en/stable/reference/reference.html#globalvar-pytest_plugins↩