今回は、Pythonのテストフレームワークpytestのレポート出力についてのメモです。
基本的なレポート出力方法と、pyest-html・フック関数を利用して、
レポート出力内容を少し調整するサンプル内容となっています。
https://pytest-html-plus.readthedocs.io/en/main/cli/cli.html?utm_source=chatgpt.com
バージョン
・Python 3.13.9
・pytest-html 4.1.1
・pytest 9.0.2
インストール
pip install pytest pytest-html py
・requirements.txt
pytest
pytest-html
py
pytest-htmlは、html形式のレポート出力用に利用します。
pyは、レポート内容をカスタマイズする際HTML要素を追加するために利用します。
レポート出力コマンド
# HTML レポートを出力(例: `report/report.html`)
pytest --html=ファイルパス/ファイル名.html --self-contained-html
# JUnit XML を出力(例: `report/report.xml`)
pytest --junitxml=ファイルパス/ファイル名.xml
※xmlは、CI実行を行いGitlabなどのツールで集計用途で作成することがあります。
レポートタイトル指定
レポートに表示したいタイトルなどの指定例です。
・conftest.py
from py.xml import html
def pytest_html_report_title(report):
# レポートの HTML タイトルを設定
report.title = "テストレポートタイトル: プロジェクト名"
def pytest_configure(config):
# レポート右上の Environment / Metadata を追加・編集
# 既存キーを消したければ pop を使う
meta = getattr(config, "_metadata", None)
if meta is not None:
meta["Project"] = "プロジェクト名"
meta["Build"] = "2025.12.22"
meta.pop("Packages", None) # 不要な既定情報を削除する例
def pytest_html_results_summary(prefix, summary, postfix):
# レポートのタイトル下などの任意の追記(HTML 要素を追加)
prefix.append(html.h2("追加情報"))
prefix.append(html.p("ビルド: 2025.12.22"))
prefix.append(html.p("実行者: testuser"))
レポート項目追加
以下はアノテーションで、説明を指定しレポート表示できるようにする実装サンプルです。
・conftest.py
from py.xml import html
DESCRIPTION_MAP = {}
DESCRIPTION_INDEX = 1 # 固定インデックス(ここを変更すれば列位置を変えられる)
def pytest_configure(config):
# マーカーだけは登録しておく(説明マーカーの警告回避)
config.addinivalue_line("markers", "description(text): test description")
# item(pytest Item)から `description` マーカーの最初の引数を取り出して返す。
# - マーカーが無い、または引数が無ければ空文字を返す(表示しない扱い)。
# - マーカーは個々のテスト関数の装飾で付ける想定。
# - パラメータ化テストやクラス/モジュールに付与したマーカーの取り扱いは
# 必要に応じてここで拡張可能。
def _get_description(item):
m = item.get_closest_marker("description")
if m and m.args:
return str(m.args[0])
return ""
# テスト収集完了時に呼ばれるフック。
# - 各 item.nodeid をキーに DESCRIPTION_MAP を作成することで
# 後でレポート行に対応する説明を検索できるようにする。
# - nodeid 例: tests/test_XXX.py::test_関数名
# - 注意: report.nodeid と完全一致することが前提。xdist などで nodeid が変わる場合は
# マッチ処理を工夫する(部分一致や正規化)必要がある。
def pytest_collection_modifyitems(config, items):
for item in items:
DESCRIPTION_MAP[item.nodeid] = _get_description(item)
# HTML テーブルのセルリストに安全に挿入するユーティリティ。
# - index が現在のセル数以上なら末尾に追加する(拡張対応)。
# - そうでなければ指定位置に挿入して列順を保つ。
# - 負の index を渡すと Python の挙動になるため、必要ならチェックを追加しても良い。
def _safe_insert(cells, index, element):
if index >= len(cells):
cells.append(element)
else:
cells.insert(index, element)
# pytest-html のテーブルヘッダーに「Description」列を挿入するフック。
# - DESCRIPTION_INDEX で列位置を制御する。
# - 他のプラグインやカスタム列と位置が被らないように index を決めること。
def pytest_html_results_table_header(cells):
_safe_insert(cells, DESCRIPTION_INDEX, html.th("Description"))
# pytest-html の各テスト行を描画するときに呼ばれるフック。
# - report.nodeid をキーに DESCRIPTION_MAP から説明を取り出して表示する。
# - マップに存在しない(収集時に説明が無かった、あるいは nodeid がずれた)場合は空文字を入れる。
# - nodeid の不一致で説明が出ない問題が起きる場合は、収集時とレポート時の nodeid を
# デバッグ出力して比較すると原因特定しやすくなるかもしれない。
def pytest_html_results_table_row(report, cells):
desc = DESCRIPTION_MAP.get(report.nodeid, "")
_safe_insert(cells, DESCRIPTION_INDEX, html.td(desc))
・テストケース
import pytest
@pytest.mark.description("説明")
def test_XXX():
今回のメモは以上となります。
pytestは、デフォルトのレポート出力は簡素なものになっていますが、
比較的自由にカスタマイズできますので、
案件の都合に合わせて調整できます。
こちらのの内容は、pyest-htmlを利用する内容になっていますが、
pytest-html-plusや他のプラグインなどもいくつか提供されていますので、
合うものを選択することをおすすめします。

