python-testing について
信頼できる品質ゲートや複数バージョンの検証が必要な Python パッケージ向けに、テスト基盤の構築を支援するスキルです。pytest 設定、ブランチカバレッジ、Hypothesis、ミューテーションテスト、複数バージョンでの実行を扱います。
主な機能
- 01pytest 設定の監査と一本化
- 02ブランチカバレッジの基準設定
- 03カバレッジゲートの失敗確認
- 04Hypothesis によるプロパティテスト
- 05ミューテーションテストによる検証
- 06複数 Python バージョンでの実行
活用シーン
- 01Python パッケージの pytest 設定
- 02機能しないカバレッジゲートの防止
- 03テストがコードの欠陥を検出するか確認
- 04複数 Python バージョンの互換性検証
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ paldom/ python-skills --skill python-testingインストール後、Claude Code で/python-testingを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
evals/evals.jsonreferences/beyond-coverage.mdconfiguration.mdmulti-version.mdscripts/check_test_config.py詳細
python-testing
負荷がかかっても信頼性を保てる Python パッケージのテスト基盤を構築します。インストール済みの pytest が実際に読み込む設定、失敗することを実証したブランチカバレッジのゲート、コードを実行するだけで検証していないテストを補う Hypothesis とミューテーションテスト、複数バージョンでの実行を整備します。このスキルが防ぐのは、成功しているように見えて実際には意味のない状態です。ゲートが何も通知せずに通過したり、設定テーブルが無視されたり、何も証明できなくなるまで人間やエージェントがテストスイートを変更したりする状態です。
使用しない場合
- 単一のファイルや関数のテストを書く場合、または特定のモジュールのテストを改善する場合は、直接対応してください。インフラは必要ありません。
- 失敗するテストを1つ診断または修正する場合は、そのテストとテスト対象のコードを読んでください。
- CI ワークフロー YAML を扱う場合(GitHub Actions のジョブ、バージョンマトリックスの設定、CI の各ジョブ間でのカバレッジのアップロード/結合など)。インストールされている場合は、python-ci スキルが担当します。このスキルは「何を」実行するかを決め、CI は「どこで」実行するかを決めます。
- パッケージング作業(pyproject のメタデータ、ビルドバックエンド、パッケージングを目的としたリポジトリの src レイアウトへの移行)には使用しないでください。インストールされている場合は、python-packaging スキルを使用してください。
- Git フックの設定には使用しないでください。python-precommit スキルを使用してください。いずれにせよ、テストスイート全体を pre-commit フックに含めるべきではありません。
ワークフロー
1. リポジトリを調査する
まず読み取り専用の監査を実行してください。設定のシャドーイングや分散を検出できます。
python3 "${CLAUDE_SKILL_DIR}/scripts/check_test_config.py" --root .
次に、後続の手順で必要となる事実を確認します。
uv run pytest --version # major version decides the config table (step 2)
grep -n "requires-python" pyproject.toml
ls pytest.ini setup.cfg tox.ini .coveragerc noxfile.py 2>/dev/null
レイアウトを確認してください。src/<pkg>/ とその兄弟ディレクトリの tests/ を使う src レイアウトか、リポジトリのルートにパッケージがあるフラットレイアウトです。全体を通じて、ツールは uv 経由で実行します(uv run、uvx)。uv が使えない場合は、通常の代替手順として python -m pip install -e ".[dev]" を実行してから python -m pytest を実行します。フラグはどこでも同じなので、ここで一度だけ記載します。
2. pytest の設定 — pyproject.toml にテーブルを1つだけ置く
テーブルは2種類あり、インストールされている pytest のメジャーバージョンによって使われるものが決まります。pytest >= 9.0(2025年11月)ではネイティブの [tool.pytest] テーブル(実際の TOML 配列/ブール値)を読み込みます。サポート対象のすべての pytest では [tool.pytest.ini_options](INI 形式の文字列)を読み込みます。両方を記述しないでください。pytest が9未満の場合、[tool.pytest] は何も通知せずに無視されます。エラーは発生せず、テストは実行されますが、厳格化フラグは適用されません。そのため、まず uv run pytest --version を確認してください(https://docs.pytest.org/en/stable/reference/customize.html)。以下のブロックでは、どこでも使える ini_options を使用しています。>= 9 のリポジトリでは、同じキーを使ったネイティブテーブルを推奨します。
[tool.pytest.ini_options]
minversion = "8.0" # raise to "9.1" if you rely on the strict flags below via addopts (see gotchas)
testpaths = ["tests"]
addopts = "-ra --strict-markers --strict-config"
filterwarnings = ["error"]
--strict-markers— タイプミスのある、または宣言されていない@pytest.mark.*マーカーが、何も通知せずに実行されないのではなく、エラーになります。--strict-config— タイプミスのある設定キーがエラーになります。filterwarnings = ["error"]— 非推奨機能を、アップグレード時ではなく今すぐエラーにします。レガシーな依存関係ツリーでは警告が多くなりますが、このゲートを削除するのではなく、対象を絞った"ignore:<msg>:<category>"エントリーを追加してください。- 非同期プロジェクトの場合は、各テストにマーカーを付けずに済むよう、pytest-asyncio を開発用依存関係に追加したうえで
asyncio_mode = "auto"を追加します。 --covはaddoptsに含めないでください。すべての実行をカバレッジと結び付けると、単一テストの反復が遅くなり、デバッガーのブレークポイントに影響する場合があります。以下の明示的なコマンドと CI でカバレッジのフラグを渡してください。トレードオフとして、カバレッジを忘れないようにaddoptsに含めるチームもあります。ユーザーがそれを希望する場合は、そのままにして、1回の実行では--no-covで無効にできることを明記してください。- 設定元は1つだけにします。 pytest は
pytest.toml/.pytest.toml、pytest.ini/.pytest.ini(どちらも空でも優先されます)、pyproject.toml(いずれかのテーブルを含むもの)、tox.ini、setup.cfgの順で最初に見つかったものを使い、残りは無視します。余分なpytest.iniがあると、pyproject の設定が何も通知せずにシャドーイングされます。設定を統合してから、余分なファイルを削除してください。
3. レイアウトと検出
src レイアウト(src/<pkg>/ にパッケージを置き、テストはパッケージ内ではなく隣接する tests/ に置く形)が確実です。uv run では、pytest は インストール済み パッケージをインポートするため、インポートやパッケージングの不具合をユーザーの環境ではなくテストで検出できます(https://docs.pytest.org/en/stable/explanation/goodpractices.html)。
testpaths = ["tests"] とカバレッジの source = ["src"] の設定も、この形に合っています。
- src レイアウトに移行した直後に「pytest がパッケージを見つけられない/インポートできない」場合は、たいてい環境にパッケージがインストールされていません。
uv sync(またはpip install -e .)を実行して、再実行してください。 - テストのためだけに、フラットレイアウトのリポジトリを src レイアウトに移行しないでください。これはパッケージングの変更です(python-packaging の領域です)。フラットレイアウトでもテストは動作しますが、インストール済みパッケージである保証がなくなります。
- 共通フィクスチャは
tests/conftest.pyに置きます。
4. カバレッジ — ブランチを有効にし、ゲートを実証する
[tool.coverage.run]
source = ["src"]
branch = true
parallel = true
relative_files = true
[tool.coverage.report]
fail_under = 85
show_missing = true
skip_covered = true
exclude_lines = [
"pragma: no cover",
"if TYPE_CHECKING:",
"raise NotImplementedError",
"if __name__ == .__main__.:",
]
- 重要なのは
branch = trueです。行カバレッジは行が実行されたことを示し、ブランチカバレッジは各ifの両方の分岐が実行されたことを示します。行のみの数値は実態より高く見えます。 fail_underは 80〜90 が妥当な範囲です。100% は推奨できません。アサーションのないテストやpragmaの乱用を招きます。本当に到達不能な行は、代わりに除外してください。.coveragercが存在する場合、pyproject の[tool.coverage.*]よりも優先され、気づかないうちに設定が上書きされます。設定元は必ず 1 つにしてください。uv run pytest --cov --cov-report=term-missingで実行してください。
ゲートを実証してください。 pytest-cov 7.1 より前のバージョンでは、レポートオプションによっては集計しきい値に達していなくても終了コードが 0 になることがありました(7.1.0、2026-03 で修正)。また、丸め処理により 67% の基準に対して 66.6% でも通過したことがあります。決めつけず、実際に確認してください。
uv run pytest --cov --cov-fail-under=100 -q; echo "exit=$?"
(カバレッジが本当に 100% でない限り)ゼロ以外の終了コードになることを確認してください。終了コードが 0 の場合は、代わりに 2 段階でゲートを設定してください。まず uv run pytest --cov を実行し、次に
uv run coverage report --fail-under=85 を実行します。単独の coverage コマンドの終了コードは信頼できます。
- 並列実行(pytest-xdist 経由の
pytest -n auto)には、parallel = trueとrelative_files = trueが必要です。テストが起動するサブプロセスには[tool.coverage.run] patch = ["subprocess"](coverage >= 7.10)が必要です。pytest-cov 7 では独自のサブプロセス計測が削除されており、これがないとカバレッジが空または 0% になります。 - しきい値は最低ラインであり、品質を示す指標ではありません。アサーションのないテストでも簡単に達成できます。手順 5〜6 でこれに対処します。PR のパッチ/差分カバレッジは CI の設定項目です(インストールされている場合は python-ci スキル)。
各フラグの詳細、優先順位のルール、結合の仕組み: references/configuration.md。
5. 効果がある場面ではプロパティベーステストを使う
パーサー、シリアライザー、コーデック、アルゴリズムのコードなど、性質(ラウンドトリップ、冪等性、不変条件)を定義できる箇所には Hypothesis を追加してください。426 プロジェクトを対象にした調査では、各プロパティベーステストは平均的な単体テストのおよそ 50 倍の変異を検出することが分かっています(https://dl.acm.org/doi/10.1145/3764068)。
uv add --dev hypothesis # exact version pinned by uv.lock
from hypothesis import given, strategies as st
@given(st.text())
def test_roundtrip(s):
assert decode(encode(s)) == s
assume()でフィルタリングするより、st.integers(min_value=1)のような上限・下限のある戦略を優先してください。大半の例を棄却すると、HealthCheck.filter_too_muchが発生します。- 設定プロファイルは
tests/conftest.pyに登録します。CI では次のコマンドで負荷の高いプロファイルを実行します。pytest --hypothesis-profile=ci:
from hypothesis import settings
settings.register_profile("ci", max_examples=1000)
settings.register_profile("dev", max_examples=25)
.hypothesis/の例データベースを保持してください(コミットするか、CI でキャッシュします)。そうしないと、実行のたびに既知の反例を最初から再発見することになります。DeadlineExceededが CI マシンでのみ発生する場合は、共有ランナーの速度が遅いことが原因です。テストを削除せず、ci プロファイルでdeadlineを意図的に引き上げるか、無効にしてください。
プロパティのパターン、戦略の設計、ステートフルテスト: references/beyond-coverage.md。
6. ミューテーションテスト — テストはバグに気づきますか?
カバレッジは行が実行されたことを証明します。ミューテーションテストは、その行が誤っているときにテストが 失敗する ことを証明します。これはアサーションの品質を直接確認するもので、エージェントがテストを作成または編集する場合に特に重要です。緑色になるまで編集したテストスイートだけでは、何も証明できません。選択肢に明確な勝者はありません。制約に応じて選んでください。
| ツール | 特徴 | 注意点 |
|---|---|---|
| mutmut | 成熟しており、最もシンプルな CLI、pytest を優先 | リポジトリ全体での実行は遅いため、paths_to_mutate の範囲を絞ってください |
| cosmic-ray | セッションベースで、再開可能、分散実行に対応 | セットアップが最も大がかりで、小規模なパッケージには過剰です |
| pytest-gremlins | pytest プラグインとして実行 | 比較的新しく、採用実績は十分ではありません。まずメンテナンス状況を確認してください |
開発依存関係としてインストールすると、プロジェクト環境内で実行され、uv.lock によってバージョンが固定されます(バージョンを固定しない uvx の単発実行では、直近に公開されたものが実行されるため、再現性とサプライチェーンに問題が生じます)。
uv add --dev mutmut
uv run mutmut run
- 最初の実行対象はコアモジュール 1 つに絞ってください(
[tool.mutmut]のsource_paths、生成コードにはdo_not_mutateを使用)。その後、生き残った変異をトリアージします。生き残ったミュータントは、アサーションが不足しているか弱いことを示します。ただし、実際に同等なミュータントの場合もあるため、その場合は記録して先に進んでください。 - 初日からミューテーションスコアを CI のゲートにしないでください。対象を絞って実行するか、夜間に実行し、そのスコアは診断情報として扱ってください。mutmut 3.7.0 の未リリースノートにさえ、カバレッジ対象外の行が変異される問題、誤った生き残り、タイムアウトが kill として数えられる問題が記載されています。実務者は、85~95%のミューテーションスコアは、同じ数値の行カバレッジよりもはるかに有意義だと報告しています。
トリアージのワークフローと、エージェント時代におけるその理由: references/beyond-coverage.md。
7. 複数バージョンでの実行
プロジェクトに合ったツールを選んでください。この議論には、まだ明確な結論がありません。
| 状況 | 使用するもの |
|---|---|
| 依存関係がシンプルで、バージョン固有の分岐が少ない | シンプルな uv ループ(以下を参照) |
| 複雑なマトリックス(extras、バックエンド、C 拡張) | tox または nox(uv で高速化) |
| INI ではなく Python の設定を使いたい、動的なセッションが必要 | nox |
| すでに tox を使用している | そのまま使い、tox-uv を追加 — 高速化でき、設定の書き換えは不要です |
for v in 3.11 3.12 3.13 3.14; do # the versions requires-python promises; 3.10 is EOL 2026-10
uv run -p "$v" --with pytest --with pytest-cov pytest || exit 1
done
tox または nox を使う場合は、環境バックエンドを uv に切り替えてください(tox-uv、nox の uv バックエンド)。設定モデルを変更せずに、環境のセットアップが大幅に高速化されます。
- nox の落とし穴:リポジトリのルートにある
.python-versionファイルによって、すべてのセッションがローカルの開発バージョンに暗黙的に固定されることがあります(https://github.com/wntrblm/nox/issues/1038)。 - 各バージョンの実行で
fail_underを適用しないでください。3.13 のみで使われる分岐があると、3.10 の実行でカバレッジが低下します。ローカルでは、parallel = trueでループを実行し、その後にuv run coverage combine && uv run coverage report --fail-under=85を 1 回実行します。この分割を CI で行う場合(アーティファクト、結合ジョブ)は python-ci に任せます。
設定とトレードオフの詳しい説明: references/multi-version.md。
8. 検証
uv run pytest— 成功すること。- ステップ 4 のゲート発動のデモが、0 以外の終了コードを返すこと。
python3 "${CLAUDE_SKILL_DIR}/scripts/check_test_config.py" --root .が 0 を返すこと。- ユーザーに、設定の場所と選択したテーブル(およびその理由)、しきい値、どのバックストップ(Hypothesis、ミューテーション、複数バージョン)を組み込んだか、または先送りしたかを報告します。
出力仕様
完了の条件:
- pytest 設定のソースとカバレッジ設定のソースがそれぞれ 1 つだけであり、pytest のテーブルがインストール済みのメジャーバージョンに一致していること。
- ブランチカバレッジが有効で、
fail_underが 80~90 の範囲にあり、ゲートを見逃した場合に 0 以外の終了コードになることを実証していること。 - クリーンチェックアウトから
uv run pytestでテストスイートが成功すること。 - 要求された場合:プロファイル付きの Hypothesis 開発依存関係、最初の範囲を絞った実行をトリアージ済みのミューテーションツール、理由を明示して選択した複数バージョン実行ツールがあること。
- CI YAML を作成または変更していないこと。
失敗モードと注意点
| 症状 | 原因/対処 |
|---|---|
| しきい値未満なのにカバレッジゲートが「合格」する | pytest-cov < 7.1 の集計終了コードのバグ、または丸め処理が原因です。ゲートを実証する(手順 4)か、単独の coverage report --fail-under でゲートしてください |
| 厳格性フラグがなぜか適用されない | pytest < 9 では [tool.pytest] が(警告なく無視される)、設定を上書きする pytest.toml/pytest.ini がある、または pytest 9.0.x を使用している可能性があります。回帰バグにより addopts 内の --strict-markers/--strict-config が警告なく無視されていました(2026-06 に 9.1.0 で修正)。>= 9 では strict_markers/strict_config の ini キーを使うことをおすすめします |
pytest -n auto の実行時にカバレッジが 0% または空になる | parallel/relative_files が設定されていない可能性があります。テストから起動したサブプロセスには coverage の patch = ["subprocess"] が必要です(pytest-cov 7 では測定されなくなりました) |
| カバレッジ設定を編集しても何も変わらない | .coveragerc が pyproject より優先されます。片方を削除してください |
| ゲートスクリプトが特定の終了コードを条件にしている | ツールによって値が異なります。coverage.py の fail_under は 2、pytest-cov は 1、テストが収集されなかった場合の pytest は 5 を返します。ゼロ以外の値を条件にしてください |
branch = true のとき、fail_under = 90 でもブランチカバレッジ 85% で「合格」する | 合計値はステートメントとブランチの実行機会を合わせたもので、ブランチだけの最低値ではありません。ブランチの列を確認するか、ブランチを別途ゲートしてください |
| マトリックスの各ジョブがバージョン固有コードで失敗する | fail_under は各ジョブで適用されます。先に結合してから、一度だけゲートしてください |
| エージェントがテストを編集した直後にテストスイートが成功する | 何も証明していません。ソースコードの差分より先にテストの差分をレビューし、ミューテーションテストを実行してください。研究者は、エージェントが conftest.py を注入して成功を偽装することを示しています。そのため、エージェントが書き込みできない環境で検証してください |
| 生成されたテストは成功するが、リグレッションを検出できない | モックに依存したテストは、エージェントに見られるバイアスとして報告されています(https://arxiv.org/abs/2602.00409)。モックの呼び出し回数ではなく、公開 API に対する振る舞いのアサーションを必須にしてください |
Hypothesis がほとんどの例を拒否する(filter_too_much) | assume() を上限のあるストラテジーに置き換えてください |
| Hypothesis がバグを見つけても、次の実行では忘れてしまう | .hypothesis/ データベースが実行間で保持されていません |
CI でのみ DeadlineExceeded が不安定に発生する | 共有ランナーが遅いことが原因です。ci プロファイルの deadline を調整してください |
ファイル
scripts/check_test_config.py— pytest/coverage 設定の読み取り専用監査 (上書きされる設定元、誤った設定テーブル、ブランチカバレッジまたはゲートの不足)。 エラーを検出するとゼロ以外で終了します。リポジトリを変更することはありません。references/configuration.md— pytest と coverage の設定を詳しく解説: 優先順位、pytest-9 のテーブルに関する問題、終了コードのバグ、xdist/結合の仕組み。references/beyond-coverage.md— Hypothesis のパターンと設定、ミューテーション ツールの比較とサバイバーのトリアージ、エージェントが編集したテストスイートが成功した場合に 補完策が必要な理由。references/multi-version.md— uv ループと tox、nox の比較、uv を利用したランナー、 バージョンをまたいだカバレッジ。