はじめに
うう・・・百合子・・・ nikkieです。
先日の@pytest.mark.sphinxの補完エントリです
目次
まとめ:Sphinx拡張のE2Eを書くには
pytestを使います。
ディレクトリ配置
.
└── tests/
├── roots/
│ └── test-default/
│ ├── conf.py
│ └── index.rst
├── __init__.py
├── conftest.py
└── test_example.py
conftest.pyでは1
from pathlib import Path import pytest pytest_plugins = ["sphinx.testing.fixtures"] collect_ignore = ["roots"] @pytest.fixture(scope="session") def rootdir() -> Path: return Path(__file__).parent / "roots"
test-defaultディレクトリのSphinxプロジェクトを元に、HTMLをビルドするテスト(※先日の記事に詳しいです)
import pytest @pytest.mark.sphinx("html", testroot="default") def test_example(app): app.build() assert (app.outdir / "index.html").exists()
コード全容
rootdirフィクスチャの上書き
conftest.pyにpytest_plugins = ["sphinx.testing.fixtures"]を指定した時点で、sphinx.testing.fixturesが提供するフィクスチャ(やマーカー)が有効になります。
pytest --fixturesの出力を見ると、そこにはrootdirというフィクスチャもあります。
% pytest --fixtures
---------------- fixtures defined from sphinx.testing.fixtures -----------------
rootdir [session scope] -- .venv/lib/python3.11/site-packages/sphinx/testing/fixtures.py:45
no docstring available
実装を見に行くと
https://github.com/sphinx-doc/sphinx/blob/v8.0.2/sphinx/testing/fixtures.py#L44-L46
@pytest.fixture(scope='session') def rootdir() -> str | None: return None
Noneを返すsessionスコープのフィクスチャです。
conftest.pyに同名のrootdirフィクスチャをsessionスコープで定義することで、Noneではない値(=rootsディレクトリへのパス)を返すようにしたと理解しました。
pytestのフィクスチャのドキュメントには、overrideについての項目があります。
https://docs.pytest.org/en/stable/how-to/fixtures.html#overriding-fixtures-on-various-levels
「Override a fixture on a folder (conftest) level」を見て、
sphinx.testing.fixturesにおいてはrootdirフィクスチャはNoneを返す- 私たちが置いた
conftest.pyのディレクトリ以下ではrootdirフィクスチャは代わりにrootsディレクトリへのパスを返す
ということではないかと思いました。
conftest.pyにrootdirフィクスチャを定義した後は、同名のフィクスチャが見つかります
% pytest --fixtures
---------------- fixtures defined from sphinx.testing.fixtures -----------------
rootdir [session scope] -- .venv/lib/python3.11/site-packages/sphinx/testing/fixtures.py:45
no docstring available
--------------------- fixtures defined from tests.conftest ---------------------
rootdir [session scope] -- tests/conftest.py:10
no docstring available
rootdirフィクスチャは何をしているのか
app_paramsフィクスチャがrootdirフィクスチャを呼び出しています(ドキュメントの言葉だとrequest)。
https://github.com/sphinx-doc/sphinx/blob/v8.0.2/sphinx/testing/fixtures.py#L71-L78
@pytest.fixture def app_params( # ... rootdir: str, ):
app_paramsフィクスチャは(上のテストコードで使った)appフィクスチャから呼び出されます。
また、@pytest.mark.sphinxマーカーの処理も担っています。
rootdirフィクスチャが使われるのはこの箇所
https://github.com/sphinx-doc/sphinx/blob/v8.0.2/sphinx/testing/fixtures.py#L109-L112
if rootdir and not srcdir.exists(): testroot_path = rootdir / ('test-' + testroot) shutil.copytree(testroot_path, srcdir)
shutil.copytreeを使って、test-defaultのようなSphinxプロジェクトをsrcdirにコピーします。
srcdirはsphinx_test_tempdirフィクスチャにより、一時ディレクトリへのパスとなります。
sphinx.testing.fixturesに定義されたrootdirフィクスチャはNoneを返すので、上書きしない場合Sphinxプロジェクトをコピーするこの箇所は実行されません。
rootdirはapp_paramsフィクスチャの定義には必要、しかしその詳細はテストコードを書くユーザに委ねるという実装になっているのですね。
終わりに
Sphinx拡張のE2Eの書き方の理解がだいぶ深まりました。
マーカーに加え、rootdirフィクスチャの定義だけでよいので、かなり簡単にE2Eを書き始められますね!
ユーザに詳細を定義させるフィクスチャというのも興味深いと思いました。
参考文献
コミッターtk0miyaさんによるquick note
https://github.com/sphinx-doc/sphinx/issues/7008#issuecomment-573092764
Sphinxのテストコードより、conftest.py
https://github.com/sphinx-doc/sphinx/blob/v8.0.2/tests/conftest.py#L35-L45