概要
pytestのcoverage.pyとpytest-cov
pytestはPythonのテスティングフレームワークの1つであり、Python標準モジュールに組み込まれているunittestと並んでPythonのテスト実装でよく使われています
pytestのカバレッジ測定ツールとしてcoverage.py
とpytest-cov
がよく利用されます。これらはPythonプロジェクトのテストカバレッジを測定するためのツールで、特にユニットテストや機能テストのコードカバレッジを評価するのに適しています。
coverage.py
coverage.py
は、Pythonのテストカバレッジ計測で最も広く使用されているツールで、ソースコードのどの部分がテストによって実行されたかを確認します。また後述するC0、C1カバレッジ計測をすることが可能です。
- 機能概要
- コードカバレッジの測定: 実行された行、未実行の行、または部分的に実行された行を追跡し、カバレッジ率を算出。
- 詳細なレポート生成: ターミナル出力、HTMLレポート、XMLレポートなどの多様な形式で結果を表示。HTMLレポートは、視覚的なインターフェースを提供し、どの行がカバーされていないかを色分けで表示。
- 実行オプションの柔軟性:
coverage run
でPythonスクリプトの実行から直接カバレッジの測定が可能。また、他のテストフレームワークと組み合わせて使用することもできます。
pytest-cov
pytest-cov
は、coverage.py
とpytest
の機能を統合するためのプラグインで、pytestコマンドと一緒に使用することでテストのカバレッジ測定を簡素化します。
- 機能概要
- シンプルな設定:
pytest --cov
コマンドだけで、対象ディレクトリやファイルに関する詳細なカバレッジ情報が得られる。 - 細かい設定:
--cov-report
オプションを使用して、ターミナル出力やHTML形式、XML形式など、複数の形式でレポートを生成。 - 他のオプションとの連携:
pytest
の他のオプションと併用することで、スキップやマッチング、ログ記録などの機能と組み合わせてカバレッジテストをカスタマイズ。
- シンプルな設定:
テストカバレッジ
カバレッジの種類
テストカバレッジとはテストがソフトウェアコードのどの程度を検証しているかを示す指標で、ソフトウェアの信頼性や品質を確保するために使用されます。テストカバレッジでは以下の4つの指標を指します。
- C0カバレッジ
基本的なコードの実行確認に特化し、未テストの部分を洗い出すのに役立ちます。これが基盤としてすべてのコードを網羅しているかを確認するための第一歩になります。 - C1カバレッジ
分岐に注目しすべてのifやelse条件をテストすることを目的としていますが、個々の条件内の組み合わせまではテストしません。 - C2カバレッジ
条件内の全パターンの組み合わせを網羅するため、特定の条件が複数ある場合や分岐の組み合わせをテストするのに有効です。 - MCC
コードの複雑性を評価し、リファクタリングやテスト計画の指針として活用できます。
通常の開発ではC0カバレッジとC1カバレッジを基準とし80%~90%程度を目安にすることが多いです。80%の根拠としてはテストの質と実装の効率性を加味したときの経験則として語られることが多いです。(例えばこちらのアトラシアンの解説でもカバレッジの目安が80%程度とされています)
テストの実行は各カバレッジを組み合わせて確認することが望ましいです。というのも各カバレッジは異なるリスク(未実行コード、未考慮条件など)を識別するため、いずれか1つのカバレッジだけではすべての欠陥を発見することができません。
カバレッジ計測の注意点
例えば下記のようなコードがあったとします。この関数に対するテストを実装する際、テストカバレッジを100%にするにはnum=0
のときとnum=1
をテストとして実装すればOKです。しかしテスト品質の観点では、それ以外にもnum=8
やnum="1"
(“1″は文字列)のときも確認することが望ましいです。つまりカバレッジが100%というのはあくまでテストケースの網羅具合を示す一つの指標であり、これとは別にテストケースの内容も検討する必要があります。
# 数字を曜日に変換
def get_weekday(num):
weekdays = { 1: "月曜", 2: "火曜", 3: "水曜", 4: "木曜", 5: "金曜", 6: "土曜", 7: "日曜" }
# 引数が1から7の範囲内であることを確認
if num not in weekdays:
raise ValueError("引数は1から7の範囲で指定してください")
return weekdays[num]
カバレッジの計測
環境構築と設定
インストール
必要なパッケージをインストールします。本作業は仮想環境を実行してから実行することが望ましいです。
pip install pytest
pip install coverage
pip install pytest-cov
.coveragercの設定
インストールが終わったらカバレッジ動作設定を行うため.coveragerc
を作成します。これはcoverage.py
の設定ファイルで、測定の対象や除外する範囲などを指定できます。設定を適切に行うと例えばテストフォルダをカバレッジ測定から除外したり、レポートの詳細設定を行ったりするとができます。様々な設定ができますのでcoverage.py
の公式サイトを確認しながら最適な設定をしてみてください。
下記は.coveragerc
のサンプルになります。基本的に[run]の設定は行うようにしましょう。
[run]
# coverage実行時の設定
branch = True # C1カバレッジ(分岐網羅)を有効にする
omit =
tests/* # テスト用フォルダをカバレッジ測定から除外する
[report]
# coverageレポート出力時の設定
show_missing = True # カバレッジがない行をレポートに表示
fail_under = 80 # カバレッジが80%未満の場合にテストを失敗とする
[html]
# HTMLレポートの設定
directory = coverage_html_report # HTMLレポートの出力先ディレクトリ
[paths]
# パスをマッピングして異なる環境間のソースコードパスを一致させる
source =
src
/home/user/project/src
ディレクトリ構成
それでは動作の確認に移ります。今回は下記のディレクトリ構成とファイルを準備してください。
.
├── .coveragerc # 設定ファイル
├── weekday.py # テスト対象ファイル
└── tests
└── test_weekday.py # テストファイル
# .coveragerc
[run]
branch = True
[report]
show_missing = True
# weekday.py
# 数字を曜日に変換
def get_weekday(num):
weekdays = { 1: "月曜", 2: "火曜", 3: "水曜", 4: "木曜", 5: "金曜", 6: "土曜", 7: "日曜" }
# 引数が1から7の範囲内であることを確認
if num not in weekdays:
raise ValueError("引数は1から7の範囲で指定してください")
return weekdays[num]
# tests/test_weekday.py
import pytest
import sys
import os
# ルートディレクトリをパスに追加
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
from weekday import get_weekday
def test_get_weekday_valid():
# 有効な入力に対するテスト
assert get_weekday(1) == "月曜"
assert get_weekday(5) == "金曜"
assert get_weekday(7) == "日曜"
def test_get_weekday_invalid():
# 無効な入力に対するテスト
with pytest.raises(ValueError):
get_weekday(0)
with pytest.raises(ValueError):
get_weekday(8)
カバレッジ計測コマンド
coverageコマンド(下記のpytestコマンドでも可)
まずcoverage
コマンドを説明します。2行目では.coveragerc
の設定を使用して pytest
を実行し、カバレッジを計測します。coverage report
でターミナルにカバレッジレポートを出力し、coverage html
でHTML形式でカバレッジレポートを生成します(htmlcov
フォルダに保存されます)。
# coverage.py のみで実行する場合
coverage run --rcfile=.coveragerc -m pytest
coverage report
coverage html
pytestコマンド
続いてpytestによる実行を説明します。上記でpytest-covをインストールしないと下記のカバレッジ計測ができないことに注意ください。--cov
ではカバレッジを測定する対象のモジュール名を指定、-cov-branch
では分岐岐カバレッジ(C1カバレッジ)を測定、--cov-config
では .coveragerc
の設定の読み込み、--cov-report
ではhtmlレポート出力を設定します。
補足になりますが、出力についてはデフォルトでターミナル出力 (--cov-report=term
) のみが有効です。またC0カバレッジ(命令網羅)でのみ実行したい場合は、.coveragerc
から branch = True
の設定を削除するか、コマンドラインで --cov-branch
を指定せずに実行します。
# ターミナルに出力する場合
pytest --cov=weekday --cov-branch --cov-config=.coveragerc
# htmlファイルで出力する場合
pytest --cov=weekday --cov-branch --cov-config=.coveragerc --cov-report=html
出力
カバレッジ計測後のディレクトリ構成
コマンド実行後のディレクトリ構成は次のようになります。
ここで2行目の.coverage
は、カバレッジ計測の実行結果が記録されたファイルです。このファイルには各行の実行情報などが保存され、coverage
ツールでレポートを生成する際に使用されます。このファイルは通常、HTML レポートやターミナル表示用に結果を再生成するための中間ファイルとして機能します。また7行目のindex.html
をブラウザで開くとwebページでカバレッジ計測結果をかくにんすることができます。
.
├── .coverage
├── .coveragerc
├── .pytest_cache
├── __pycache__
├── htmlcov
│ ├── index.html
│ └── ...
├── tests
│ ├── __pycache__
│ └── test_weekday.py
└── weekday.py
ターミナルに出力した場合
(myenv) [ec2-user@ip-xxx.xxx.xxx.xxx pytest]$ pytest --cov=weekday --cov-branch --cov-config=.coveragerc
================================================================ test session starts =================================================================
platform linux -- Python 3.9.16, pytest-8.3.3, pluggy-1.5.0
rootdir: /home/ec2-user/myenv/pytest
plugins: cov-6.0.0
collected 2 items
tests/test_weekday.py .. [100%]
---------- coverage: platform linux, python 3.9.16-final-0 -----------
Name Stmts Miss Branch BrPart Cover Missing
--------------------------------------------------------
weekday.py 5 0 2 0 100%
--------------------------------------------------------
TOTAL 5 0 2 0 100%
HTMLファイルで出力する場合
HTMLファイルで出力した場合、index.htmlを開くと下記のような画面に遷移します。これはレポートの概要を表示するトップページになります。ここで赤枠のファイル名をクリックします。
下図は詳細のページになります。もし計測漏れがあるコードがある場合、このページで確認することができます。
まとめ
本記事ではpytestのカバレッジ計測方法について解説しました。本文でも述べましたがカバレッジ計測には種類がありるためそれぞれの特性を理解して計測しましょう。また計測した数値が高いだけでは品質が担保できません。うまくカバレッジをモニターしながらテスト品質を高めていくようにしていきましょう。