pytestのcoverage.pyとpytest-covでカバレッジを計測しよう!

eyecatch-pytest-coverage
目次

概要

pytestのcoverage.pyとpytest-cov

pytestはPythonのテスティングフレームワークの1つであり、Python標準モジュールに組み込まれているunittestと並んでPythonのテスト実装でよく使われています

pytestのカバレッジ測定ツールとしてcoverage.pypytest-covがよく利用されます。これらはPythonプロジェクトのテストカバレッジを測定するためのツールで、特にユニットテストや機能テストのコードカバレッジを評価するのに適しています。

coverage.py

coverage.pyは、Pythonのテストカバレッジ計測で最も広く使用されているツールで、ソースコードのどの部分がテストによって実行されたかを確認します。また後述するC0、C1カバレッジ計測をすることが可能です。

  • 機能概要
    • コードカバレッジの測定: 実行された行、未実行の行、または部分的に実行された行を追跡し、カバレッジ率を算出。
    • 詳細なレポート生成: ターミナル出力、HTMLレポート、XMLレポートなどの多様な形式で結果を表示。HTMLレポートは、視覚的なインターフェースを提供し、どの行がカバーされていないかを色分けで表示。
    • 実行オプションの柔軟性: coverage runでPythonスクリプトの実行から直接カバレッジの測定が可能。また、他のテストフレームワークと組み合わせて使用することもできます。

pytest-cov

pytest-covは、coverage.pypytestの機能を統合するためのプラグインで、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=8num="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のカバレッジ計測方法について解説しました。本文でも述べましたがカバレッジ計測には種類がありるためそれぞれの特性を理解して計測しましょう。また計測した数値が高いだけでは品質が担保できません。うまくカバレッジをモニターしながらテスト品質を高めていくようにしていきましょう。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

Hack Luck Labの管理人hakula(ハクラ)です。2012年にSIerに新卒入社し、SE、新規事業、情シスを担当。その後、ITコンサルを経て、現在はバックエンドエンジニア。過去にはC#、SQL Server、JavaScriptで開発を行い、現在はPython、Rest Framework、Postgresql、Linux、AWSなどを使用しています。ノーコードツールやDX関連も興味あり。「技術は価値を生むために使う」ことが信条で、顧客や組織への貢献を重視しています。

目次