0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

#0219(2025/08/20)pytestのカバレッジ初級編

Last updated at Posted at 2025-08-20

pytestのカバレッジ初級編 — 実務で迷わない最短ルート

カバレッジとは「テストがどのコードをどれだけ通ったかを数値化する指標」である。


なぜカバレッジを測るのか(目的と効果)

  • 回 regressions の早期発見:未テストの行・分岐は不具合の温床。
  • レビューの焦点化:未実行の行番号が“穴”を可視化し、レビューや追加テストの優先度がつく。
  • 継続的改善:CIで基準値を設定し、下回ったら失敗させることで品質を維持。

カバレッジの基本概念(行・分岐・条件)

カバレッジは大きく分けて、コードを実行したか(行)と、条件分岐の真偽両方を通したか(分岐)で捉えると理解が早い。

カバレッジ種類の比較

種類 測る対象 何が分かる 強み 注意点 pytestオプション
行(Line) 実行された行の割合 どの行が実行されたか セットアップが容易、理解しやすい 条件の片側だけでも“通った”扱い --cov=PKG
分岐(Branch) 条件のTrue/False 各分岐の網羅状況 例外経路やガード節の穴を炙れる やや遅くなる、目標設定が難しい --cov-branch
条件(Condition) 複合条件の項目単位 a and b の a/b それぞれ より厳密 初級は過剰になりやすい (coverage.pyの詳細設定)

初級編の実務では 「行 + 分岐」 を抑えれば十分。


最小コマンドと読み方(まずはこれ)

pytest -q -ra \
  --cov=my_app \
  --cov-branch \
  --cov-report=term-missing
  • -q: 出力を簡潔に
  • -ra: skip/xfail などの理由をまとめ表示
  • --cov=my_app: 対象パッケージを指定
  • --cov-branch: 分岐カバレッジを有効化
  • --cov-report=term-missing: ターミナルに未実行の行番号を出す

出力の読み方(例)

Name              Stmts   Miss Branch BrPart  Cover   Missing
------------------------------------------------------------
my_app/core.py       80      5     22      3    89%   45-47, 90, 113
  • Miss: 未実行の行数
  • Branch: 分岐数 / BrPart: 片側だけ通った分岐の数
  • Missing: 未実行行の具体的な行番号

レポート形式の比較(用途別)

レポート 使いどころ 特徴 具体例
term ざっくり進捗確認 標準出力に要約 CIログに軽量表示
term-missing 穴潰しの作業 未実行行の行番号が出る ローカルでの追加テスト設計に最適
html 視覚的に穴を追跡 行ごとに色分けハイライト --cov-report=htmlhtmlcov/ を開く
xml ツール連携 CIのゲートやダッシュボード --cov-report=xml

穴を埋める作業term-missing / チーム共有html が強い。


目標値と失敗基準(CIでのゲーティング)

pytest --cov=my_app --cov-branch --cov-fail-under=85
  • 初期目標の目安:

    • 行カバレッジ: 80–90%(現実的ライン)
    • 分岐カバレッジ: 60–80%(まずは主要分岐を通す)
  • クリティカルなモジュール(計算・料金・権限)は 局所的に 100% を狙う。

「全体 85%、重要部は 100%」の二層目標が実務的。


ありがちな落とし穴と回避策

  • 100% = バグゼロではない:仕様漏れ・相互作用は別問題。プロパティテスト境界値と併用。
  • ガード節が未通過if not x: return の否定側が未テストになりがち → 異常系テストを追加。
  • 例外経路の未網羅try/except の except ブロックが空きやすい → 擬似的に例外を起こす。
  • I/O・時刻依存:モック/フリーズタイムで安定化。
  • 並列実行(xdist)時の集計--cov はプロセス間結合をサポート。必要に応じて --cov-context=test でテスト別文脈も可。
  • 型ヒント分岐・デバッグ行if TYPE_CHECKING: / ログのみ行は # pragma: no cover で除外可。

実践レシピ(初導入~継続運用)

  1. 最小導入:上記の最小コマンドで実行し、term-missing の穴を確認。
  2. 高リスク優先:ドメイン核心部(料金、在庫、権限)から穴を埋める。
  3. 異常系の追加:分岐カバレッジを底上げ(False側/例外経路)。
  4. CI化--cov-fail-under を80→85→90と段階引き上げ。
  5. 差分カバレッジ:PRでは変更行/近傍の分岐を必ず通す(レビューの約束事に)。

.coveragerc の最小例(コピペでOK)

[run]
branch = True
source = my_app
omit =
    */migrations/*
    */__main__.py
    */_experiments/*

[report]
show_missing = True
exclude_lines =
    pragma: no cover
    if __name__ == .__main__.:
    if TYPE_CHECKING:
  • branch=True で分岐カバレッジをデフォルト有効化。
  • omit で対象外(マイグレーションなど)を除外。
  • exclude_lines で実務上ノイズになる行をスキップ。

ミニサンプル(分岐の穴を可視化)

対象コード calc.py

def tier(price: int) -> str:
    if price < 0:
        raise ValueError("negative")
    if price == 0:
        return "free"
    return "paid" if price < 100 else "enterprise"

テスト test_calc.py(初期)

from calc import tier

def test_free():
    assert tier(0) == "free"

def test_paid():
    assert tier(10) == "paid"

実行

pytest -q -ra --cov=. --cov-branch --cov-report=term-missing

想定出力(抜粋)

calc.py          6      1      2      1    83%   2
  • 行2(price < 0)の例外経路が未実行、分岐の片側も未通過。

穴を埋めるテスト追加

def test_negative():
    import pytest
    with pytest.raises(ValueError):
        tier(-1)

def test_enterprise():
    assert tier(100) == "enterprise"

→ 再実行で Miss=0, BrPart=0 へ。分岐が“両側”通ったことを確認。


レポートの粒度を使い分ける

  • ローカルで穴埋め:--cov-report=term-missing
  • 俯瞰して共有:--cov-report=html をCIの成果物へ
  • ツール連携:--cov-report=xml をCodecov/Sonar等に

図:計測の流れ(シンプルモデル)

┌──────────┐    ┌──────────────┐   ┌──────────────┐
│  Tests     │──▶│ coverage.py   │──▶│ Reports       │
│ (pytest)   │    │ (測定エンジン) │   │ (term/html/xml) │
└──────────┘    └──────────────┘   └──────────────┘
         ▲                │
         │                ▼
        Code ─────────▶ Execution Tracing

実務Tips(時短と安定化)

  • 遅いテストは階層で分けるmarkers で unit/slow/integration を切り分け、単体はカバレッジ必須、統合は参考値。
  • パラメタライズで分岐網羅@pytest.mark.parametrize で True/False/境界値を一気に通す。
  • フレーク対策:ランダム性はseed固定、時間はフリーズ、ネットワークはモック。
  • 生成コード・型用分岐は除外:ノイズを削り、意味のある分子/分母を作る。

よくある質問(FAQ)

  • Q: 100%必須?不要。コストとリスクのバランス。中核ロジックは100%、周辺は80%目標が現実的。
  • Q: 低カバレッジのままでもいい? → 新規コードは基準厳しめ、既存は差分で上げる方針がおすすめ。
  • Q: xdist併用で壊れる? → 現行の pytest-cov はプロセス間集約をサポート。CIでは --cov-context=test が有用な場合あり。

最小チェックリスト(コミット前)

  • pytest -q -ra --cov=PKG --cov-branch --cov-report=term-missing
  • --cov-fail-under=XX を満たすか
  • 未実行行に対して 異常系/境界値 を追加したか
  • クリティカルモジュールは分岐の片側残しが無いか(BrPart=0
  • 除外設定の妥当性(pragma: no cover の乱用禁止)

まとめ(要点)

  • 「行+分岐」を観るのが初級の最短ルート。
  • term-missing穴を特定→ 異常系/境界値で埋める。
  • CIに --cov-fail-under を入れて継続的に上げる

今日から使うなら:このページのコマンドと .coveragerc をそのまま投入し、MissingBrPart をゼロにすることから始めよう。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?