テストを書いたあと、そのテストがコードのどこまで実行しているのかを数値で把握できると、抜け漏れに気づきやすくなります。
本記事では、pytestの拡張であるpytest-covを使ってテストカバレッジを計測し、ターミナル表示とHTMLレポートで網羅性を確認する方法を、初心者の方向けに丁寧に解説します。
Pythonのテストカバレッジとは?pytest-covの基本
テストカバレッジ(coverage)の意味
テストカバレッジは、テスト実行時にどのコードが実際に実行されたか
を割合で示す指標です。
一般的には行カバレッジ(line coverage)
が使われ、全行のうち実行された行の割合(%)を示します。
加えて分岐カバレッジ(branch coverage)
を測ると、if
など条件分岐の各経路を通ったかもわかります。
カバレッジの数値は「品質そのもの」ではありませんが、抜け落ちたテスト箇所に気づくための強力な指標です。
pytestとpytest-covの関係
pytestはPythonの標準的なテストランナーです。
pytest-covは、そのpytestにカバレッジ計測機能を追加するプラグインで、内部的にはcoverage.py
という実績あるライブラリを利用しています。
これにより、pytestのコマンドにオプションを付けるだけで、カバレッジの測定やレポート出力が可能になります。
初心者が押さえるポイント
- いつも
--cov=パッケージ名
を付けると、測定対象が明確になり、不要なライブラリまで集計されるのを防げます。 - 数値を上げることが目的化しないようにし、
--cov-report=term-missing
やHTMLレポートで「どの行が未実行か」を見て具体的に改善しましょう。 - 分岐の網羅性も確認したい場合は
--cov-branch
を使います。
pytest-covの導入と基本の使い方
インストール(pip install pytest-cov)
まずはpytest本体とpytest-covをインストールします。
仮想環境(venv)の利用をおすすめします。
# (任意) 仮想環境を作成して有効化
python -m venv .venv
# Windows
.venv\Scripts\activate
# macOS / Linux
source .venv/bin/activate
# pytest と pytest-cov をインストール
pip install pytest pytest-cov
最小の実行例(pytest –cov)
以下は最小のサンプルプロジェクトです。
プロジェクトルートでpytestを実行することが重要です。
your_project/
├─ app/
│ ├─ __init__.py # 中身は空でOK
│ └─ calculator.py
└─ tests/
└─ test_calculator.py
# app/calculator.py
# シンプルな関数をいくつか定義します。条件分岐を入れて、分岐カバレッジの例にも使います。
def add(a, b):
"""2つの数値を足し合わせます。"""
return a + b
def divide(a, b):
"""0除算を考慮しながら割り算を行います。"""
if b == 0:
# ここは例外を発生させる分岐です
raise ZeroDivisionError("b must not be zero")
return a / b
def safe_divide(a, b, default=None):
"""0除算時にデフォルト値を返す安全な割り算。default未指定ならNoneを返します。"""
if b == 0:
# 条件に応じて2通りの経路があります
return default if default is not None else None
return a / b
def greet(name):
"""名前付きの挨拶。nameが空なら 'world' になります。"""
return f"Hello, {name or 'world'}!"
# tests/test_calculator.py
# いくつかの関数だけテストして、あえて未実行の行を残します。
import pytest
from app.calculator import add, divide, safe_divide, greet
def test_add():
# 基本的な加算
assert add(2, 3) == 5
def test_divide_ok():
# 通常の割り算
assert divide(10, 2) == 5
def test_divide_zero():
# 0除算で例外が発生することを確認
with pytest.raises(ZeroDivisionError):
divide(1, 0)
def test_safe_divide_default_is_none():
# default未指定の場合の0除算はNone
assert safe_divide(1, 0) is None
# greetのデフォルト分岐や safe_divide の default 指定分岐はあえて未テストにしておきます
最小のカバレッジ計測は次のコマンドです。
pytest --cov
しかし、これだと対象が広すぎることがあるため、測定対象を明示するのが基本です。
pytest --cov=app
実行例(ターミナル出力)
============================= test session starts =============================
collected 4 items
tests/test_calculator.py .... [100%]
---------- coverage: platform linux, python 3.11.9-final-0 -----------
Name Stmts Miss Cover
-----------------------------------------
app/calculator.py 20 3 85%
-----------------------------------------
TOTAL 20 3 85%
============================== 4 passed in 0.10s =============================
対象モジュールを指定する(–cov=パッケージ名)
–cov=で対象を指定すると、その配下のPythonファイルが集計されます。
複数指定も可能です。
# 単一パッケージ
pytest --cov=app
# 複数パッケージ(例: app と utils の両方)
pytest --cov=app --cov=utils
プロジェクトルートで実行すると、相対パスの解決やインポートが安定します。
testsディレクトリの中でpytestを実行すると、パスやインポートの不整合で0%になることがあるため注意してください。
ターミナル出力(coverage)の読み方
上の例に加えて--cov-report=term-missing
を付けると、未実行の行番号が表示されて改善点が明確になります。
pytest --cov=app --cov-report=term-missing
---------- coverage: platform linux, python 3.11.9-final-0 -----------
Name Stmts Miss Cover Missing
---------------------------------------------------
app/calculator.py 20 3 85% 16, 20-21
---------------------------------------------------
TOTAL 20 3 85%
- Name: ファイル名やモジュール名です。
- Stmts: 実行対象の文数です。
- Miss: 実行されなかった文の数です。
- Cover: 行カバレッジの割合(%)です。
- Missing: 未実行の行番号の一覧です。範囲表示(20-21)は連続行を意味します。
分岐の網羅性も見たい場合は--cov-branch
を付けます。
出力にBranch
などの列が追加され、条件分岐のカバレッジを確認できます。
pytest --cov=app --cov-branch --cov-report=term-missing
HTMLレポートの出力と見方
HTMLレポートを生成する(–cov-report=html)
HTMLレポートは未実行行が色分けされ、ブラウザで直感的に確認できます。
テキスト出力と併せて生成するのがおすすめです。
# ターミナル出力とHTMLレポートを同時に生成
pytest --cov=app --cov-report=term-missing --cov-report=html
標準ではhtmlcovというディレクトリにレポートが生成されます。
タイトルやディレクトリは.coveragerc
で変更できます(後述)。
出力先(htmlcov)を開く手順
生成後、以下のいずれかの方法でhtmlcov/index.htmlを開きます。
- macOS:
open htmlcov/index.html
- Linux:
xdg-open htmlcov/index.html
- Windows:
start htmlcov\index.html
- どの環境でも使える方法: 簡易HTTPサーバで配信して開く
# プロジェクトルートで実行
python -m http.server 8000
# ブラウザで http://localhost:8000/htmlcov/index.html を開く
HTMLレポートの色と数値の見方
ファイル一覧では、各ファイルのカバレッジがパーセンテージと色で表示されます。
一般的にカバレッジが高いほど緑、低いほど赤に近い色になります。
デフォルトのしきい値は概ね高カバレッジ90%、低カバレッジ50%付近で色分けされます。
ファイルをクリックすると、#
で未実行行にマーカーが付き、上下文を確認しながらどのテストを追加すべきか判断できます。
以下は、よく使う出力オプションのまとめです。
オプション | 目的 | 例 |
---|---|---|
--cov=PACKAGE | 測定対象のソース指定 | --cov=app |
--cov-branch | 分岐カバレッジも計測 | --cov=app --cov-branch |
--cov-report=term-missing | 未実行行の行番号を表示 | --cov-report=term-missing |
--cov-report=html | HTMLレポートを生成 | --cov-report=html |
--cov-fail-under=NUM | 規定値未満でテスト失敗にする | --cov-fail-under=90 |
便利なオプションとつまずき対策
最低カバレッジを設定する(–cov-fail-under=数値)
継続的インテグレーション(CI)で一定の品質を担保したい場合、--cov-fail-under
が便利です。
指定値を下回るとpytestが失敗扱い(非ゼロ終了コード)になります。
pytest --cov=app --cov-report=term-missing --cov-fail-under=90
実行例(しきい値未達で失敗)
---------- coverage: platform linux, python 3.11.9-final-0 -----------
Name Stmts Miss Cover Missing
---------------------------------------------------
app/calculator.py 20 3 85% 16, 20-21
---------------------------------------------------
TOTAL 20 3 85%
FAILED Required test coverage of 90% not reached. Total coverage: 85.00%
PRにこの条件を組み込むと、レビュー時にカバレッジ低下を自動で検出できます。
テスト対象を限定する(複数の–cov指定)
大きなリポジトリでは対象を限定すると解析が速くなります。
複数の--cov
を並べて、必要なパッケージだけを測りましょう。
# アプリケーション本体とユーティリティを測る
pytest --cov=app --cov=utils --cov-report=term-missing
サイトパッケージなど外部ライブラリは原則測らないのが実務的です。
必要なら.coveragerc
でomit
を設定して除外できます。
# .coveragerc の例
[run]
# 必要に応じて src レイアウトなどのパスを明示
# source = app
[report]
# レポートから tests 配下を除外
omit =
tests/*
*/site-packages/*
# 欲しければ分岐カバレッジを常に有効化
# precision などの出力調整も可能
# show_missing = True
[html]
# HTMLの出力先やタイトル
directory = htmlcov
title = Project Coverage Report
よくあるつまずき(0%になる/対象外になる)
初心者が遭遇しやすいポイントを具体的に解説します。
1. –covの対象を間違えている
–covに誤ったパスやパッケージ名を指定すると、測定対象に含まれず0%になったり、何も表示されなかったりします。
常にプロジェクトルートでpytest --cov=app
のように実行し、パッケージ名はimport
に使う名前と一致させましょう。
2. srcレイアウトでインポートできていない
プロジェクトが以下のようなsrcレイアウトの場合、テスト実行時にパッケージがインポートできず、結果として実行されないことがあります。
your_project/
├─ src/
│ └─ app/
│ ├─ __init__.py
│ └─ calculator.py
└─ tests/
└─ test_calculator.py
対策はどちらかです。
pip install -e .
で編集可能インストールを行い、app
をインポート可能にする。- 一時的に
PYTHONPATH=src
を設定してpytestを実行する。 --cov=src/app
と指定しつつ、インポート経路も上記のいずれかで解決する。
3. 実行されていない行を見逃している
--cov-report=term-missing
やHTMLレポートでどの行が実行されていないかを確認しましょう。
未実行行に対応するテストを追加すると、カバレッジが意味のある形で向上します。
例えば、先ほどの例ではgreet(None)
やsafe_divide(1, 0, default=0)
の分岐が未実行でした。
# tests/test_calculator_more.py
# 未実行だった分岐を補う例です。
from app.calculator import greet, safe_divide
def test_greet_default():
# name が falsy の場合の分岐を実行
assert greet("") == "Hello, world!"
def test_safe_divide_with_default():
# default を指定した 0 除算の分岐を実行
assert safe_divide(1, 0, default=0) == 0
4. 並列実行やサブプロセスで集計できない
マルチプロセスやサブプロセスを伴うコードでは、カバレッジ収集が分散してしまうことがあります。
pytest-covは多くの場合自動で子プロセスも計測しますが、うまくいかない場合は.coveragerc
で以下の設定を検討します。
# .coveragerc の例(サブプロセス対応を強化)
[run]
concurrency = multiprocessing
parallel = True
実行後にcoverage combine
で結果を統合するアプローチもありますが、まずは単純なケースから動作確認するのが安全です。
5. 除外設定(omit/exclude)に引っかかっている
.coveragerc
のomit
やexclude_lines
が強すぎると、意図せず対象外になります。
設定を最小限にして原因を切り分け、必要に応じて徐々に広げる方が失敗しにくいです。
まとめ
テストカバレッジは、「どこをテストしていないか」を可視化するための実践的な道具です。
pytest-covを使えば、pytestに数個のオプションを足すだけで、ターミナルとHTMLの両方で結果を確認できます。
運用のコツは、--cov=パッケージ名
で対象を明確化し、--cov-report=term-missing
で未実行行を把握し、必要なテストをピンポイントに追加していくことです。
しきい値は--cov-fail-under
でルール化しつつ、数字のためのテストではなく、バグを防ぐためのテストを意識してください。
HTMLレポートを定期的に見直し、プロジェクトの成長に合わせて設定(.coveragerc)を育てていけば、テストの網羅性と開発速度の両立に近づけます。