閉じる

【Python】テスト効率化:pytest-covでカバレッジを可視化する方法

Pythonでのテストは動かして終わりではなく、「どこまでテストできているか」を把握することが重要です。

本記事では、pytestと連携してカバレッジ(網羅率)を簡単に計測できるpytest-covを取り上げ、導入から実践的な活用テクニックまで、図解を交えながら丁寧に解説します。

単なる使い方だけでなく、チーム開発やCI連携で活きる設定方法も紹介します。

pytest-covとは何か

pytest-covでできることと導入するメリット

pytest-covは、Pythonのテストランナーであるpytestにカバレッジ(テストがソースコードをどれだけ実行したか)計測機能を追加するプラグインです。

内部的にはcoverage.pyという有名なライブラリを利用しつつ、pytestから簡単に扱えるようにしてくれます。

pytest-covを導入することで、次のようなメリットがあります。

1つ目に、テスト実行とカバレッジ計測を1コマンドで行えることです。

pytestに--covオプションを付けるだけで、テストと同時にカバレッジが集計されます。

2つ目に、ターミナル、HTML、XMLといった複数形式のレポートを出力できることです。

開発者がローカルで確認しやすい形式と、CIツールで解析しやすい形式の両方を簡単に得られます。

3つ目に、測定対象・除外対象を柔軟に制御できることです。

アプリケーションコードだけを計測し、テストコードや仮想環境、マイグレーションスクリプトなどを除外する設定が可能です。

このように、pytest-covを使うことで、テストの「量」だけでなくテストの網羅性も把握できるようになり、テスト戦略の改善に役立ちます。

カバレッジ計測の基本概念

カバレッジは、テストがソースコードをどの程度実行したかを定量的に示す指標です。

pytest-covでは主に行カバレッジ(Line Coverage)を扱いますが、coverage.pyの設定によっては分岐カバレッジなども計測可能です。

代表的な種類は次の通りです。

行カバレッジでは、実際に実行された行の割合を測定します。

例えば100行あるコードのうち、テストで60行が一度でも実行されていれば、行カバレッジは60%になります。

分岐カバレッジは、if文などの分岐ごとに、すべてのパス(真/偽)が通ったかを評価します。

行カバレッジが十分でも、分岐カバレッジが低い場合、条件分岐の片方しかテストできていない可能性があります。

カバレッジ値は高ければ高いほど良いとされがちですが、100%なら完全にバグがないという意味ではありません。

あくまでテスト網羅性のヒントとして利用し、他の品質指標と組み合わせて判断することが重要です。

pytest-covの導入と基本的な使い方

pytest-covのインストール手順

pytest-covはPyPIで配布されているため、pipで簡単にインストールできます。

プロジェクト単位で導入する場合は、仮想環境を有効にしてから実行することをおすすめします。

Shell
# 仮想環境を作成・有効化していることを前提とします
pip install pytest pytest-cov

Poetryやpipenvを利用している場合は、それぞれのパッケージ管理ツールからインストールしてください。

Shell
# Poetryの場合
poetry add --dev pytest pytest-cov

# pipenvの場合
pipenv install --dev pytest pytest-cov

インストール後、pytest --helpを実行すると、pytest-covが追加したオプション群(--covなど)が表示されていることを確認できます。

Shell
pytest --help | grep -- --cov

シンプルなカバレッジ計測コマンドの例

ここでは、シンプルなサンプルコードとテストコードを用意し、pytest-covでカバレッジを計測する流れを見ていきます。

まず、calculator.pyというモジュールと、そのテストtest_calculator.pyを用意します。

Python
# calculator.py

def add(a: int, b: int) -> int:
    """2つの整数を加算して返します"""
    return a + b


def divide(a: int, b: int) -> float:
    """2つの数値を割り算します。0除算時はZeroDivisionErrorを送出します"""
    # 想定上はこの関数もテストしたいが、あえて一部テストを欠かせることで
    # カバレッジの違いを確認できるようにします
    return a / b
Python
# test_calculator.py

from calculator import add, divide


def test_add():
    """add関数の基本動作をテストします"""
    assert add(1, 2) == 3
    assert add(-1, 1) == 0
    assert add(0, 0) == 0


def test_divide():
    """divide関数の正常系をテストします"""
    assert divide(4, 2) == 2.0

上記ファイルを同じディレクトリに置いた状態で、次のようにpytest-covを実行します。

Shell
pytest --cov=calculator test_calculator.py

ここで--cov=calculatorは、calculatorモジュールをカバレッジ計測の対象とする意味です。

test_calculator.pyは、実行するテストファイルを指定しています。

実行すると、ターミナルの最後にカバレッジサマリが表示されます。

この出力については、後ほど「ターミナルでのカバレッジレポート表示」で詳しく説明します。

特定モジュール・パッケージだけを計測する方法

実際のプロジェクトでは、次のような要望がよくあります。

「アプリケーションコードだけカバレッジ計測し、テストコードや外部ライブラリは除外したい」

pytest-covでは、--covオプションを複数回指定することで、対象モジュール・パッケージを細かく制御できます。

Shell
# myappパッケージ全体をカバレッジ計測し、tests配下は実行のみ(計測対象外)とする例
pytest --cov=myapp tests/

複数パッケージを指定することも可能です。

Shell
pytest --cov=myapp --cov=utils tests/

この場合、myapputils配下のコードだけがカバレッジ対象になり、その他のモジュールはカバレッジ集計の対象外になります。

また、プロジェクトルート以下を一括で対象にしたい場合は、パスで指定することもできます。

Shell
pytest --cov=./src tests/

ただし、あまりに広く指定するとテストコードや設定スクリプトまで含まれてしまうため、アプリケーションコードが入っているディレクトリ単位で指定するのが実務的にはおすすめです。

カバレッジレポートの可視化と活用

ターミナルでのカバレッジレポート表示

pytest-covを--covだけで実行すると、標準ではターミナルにシンプルなテキストレポートが出力されます。

Shell
pytest --cov=calculator test_calculator.py

実行結果(イメージ)は次のようになります。

---------- coverage: platform linux, python 3.11.0-final-0 ----------
Name              Stmts   Miss  Cover
-------------------------------------
calculator.py         6      1    83%
-------------------------------------
TOTAL                 6      1    83%

ここで重要なのは次の列です。

  • Name: ファイル名
  • Stmts: 実行可能なステートメント(行)数
  • Miss: テストで一度も実行されなかった行の数
  • Cover: カバレッジ(網羅率)

例えば上記例では、6行のうち1行が未実行、カバレッジは83%ということを意味します。

Missが1行だけでも、どの行が不足しているかまではこの表では分からないため、詳細を知りたい場合はHTMLレポートを併用します。

HTMLレポートでカバレッジを可視化する方法

HTMLレポートを生成すると、ブラウザ上でどの行がテストされていて、どの行がテストされていないかを視覚的に確認できます。

pytest-covでは、--cov-report=htmlオプションを追加するだけでHTMLレポートを出力できます。

Shell
pytest --cov=calculator --cov-report=html test_calculator.py

実行後、プロジェクト直下にhtmlcovというディレクトリが生成され、その中のindex.htmlをブラウザで開くとレポートが閲覧できます。

Shell
# macOSやLinuxでは次のようにブラウザで開けます
open htmlcov/index.html

# Windowsの場合(環境によって異なります)
start htmlcov/index.html

HTMLレポートでは、ファイル一覧とファイルごとの詳細が表示され、未テスト行が赤で強調されます。

特に、複雑なロジックを含む関数で赤い行が残っていないかを確認することで、テスト不足の重要箇所を効率よく見つけられます。

XMLレポートをCIツール連携に活用する方法

CIツールやコード品質プラットフォーム(SonarQubeなど)とカバレッジを連携させたい場合、XML形式のレポートを使うことが多くあります。

pytest-covでは--cov-report=xmlオプションでXMLレポートを出力できます。

Shell
pytest --cov=myapp --cov-report=xml tests/

このコマンドを実行すると、プロジェクトルートにcoverage.xmlというファイルが生成されます。

CI側ではこのファイルを読み込んで、以下のような用途に活用できます。

  • プルリクエストごとのカバレッジ変化を表示する
  • カバレッジの閾値に満たない場合にビルドを失敗させる
  • ダッシュボード上でサービス全体のカバレッジ推移を可視化する

XMLレポートの具体的な連携手順は、使用するCIツールやサービスごとに異なりますが、pytest-cov側ではXMLを出力するだけでよいことが多く、導入のハードルは比較的低いです。

カバレッジ結果からテスト不足箇所を特定するコツ

カバレッジレポートを眺めるだけではなく、どこからテストを補っていくかを考えることが重要です。

効率よくテスト不足箇所を特定するためのポイントをいくつか挙げます。

まず、カバレッジが特に低いファイルやモジュールから優先的にチェックします。

ビジネスロジックの中心となるモジュールなのにカバレッジが低い場合は、影響範囲が大きいため優先してテストを追加すべきです。

次に、HTMLレポートを使って未テスト行のパターンを把握します。

特に不足しがちな箇所としては、次のようなものがあります。

  • 条件分岐の片側だけが実行されているif-else構文
  • エラー時の動作を確認していない例外処理のブロック
  • 入力値の境界条件(0、空文字、Noneなど)に対するパス
  • ログ出力や通知など、副作用を伴うコード

最後に、カバレッジだけを目的化しないことも大切です。

カバレッジを100%にすること自体よりも、「重要なビジネスロジック」「バグが起きやすい複雑な部分」「修正が頻発する箇所」を優先してカバーすることが、実務的には効果的です。

テスト効率化のためのpytest-cov活用テクニック

カバレッジ閾値を設定して品質を担保する

pytest-covでは、カバレッジが一定値を下回った場合にテストを失敗させることができます。

これにより、カバレッジの下振れを自動的に検知し、品質の維持に役立てることができます。

代表的なのが--cov-fail-underオプションです。

Shell
# カバレッジが80%を下回った場合にテストを失敗させる
pytest --cov=myapp --cov-report=term --cov-fail-under=80 tests/

カバレッジが80%未満の場合、pytestの終了コードが非ゼロとなり、CI環境ではジョブが失敗として扱われます。

チーム運用では、いきなり高い値を設定するのではなく、現在のカバレッジより少し低めの値から始めて、徐々に引き上げていくのがおすすめです。

これにより、既存の負債を少しずつ減らしつつ、新規コードの品質も保ちやすくなります。

計測対象から除外するファイル・ディレクトリ指定

実プロジェクトでは、すべてのファイルをカバレッジ対象にするのではなく、テストすべき対象コードに絞って測定したいケースがほとんどです。

pytest-covおよびcoverage.pyでは、いくつかの除外方法が用意されています。

1つは、pytestのオプションで指定する方法です。

--covで測定対象ディレクトリを絞るだけでも多くの場合は十分です。

より細かい制御が必要な場合は、.coveragercファイルを用いるのが一般的です。

.coveragercはcoverage.pyの設定ファイルで、pytest-covもこれを参照します。

INI
# .coveragerc

[run]
# カバレッジ計測の対象をsrc配下に限定
source = src

[report]
# レポートから除外するパターン
omit =
    src/myapp/migrations/*
    src/myapp/__init__.py
    */tests/*

この設定を用意した上で、pytestを次のように実行します。

Shell
pytest --cov=src --cov-report=term-missing tests/

ここでterm-missingレポートを指定すると、ターミナルに未テスト行の詳細も表示されます。

実行結果
---------- coverage: platform linux, python 3.11.0-final-0 ----------
Name                  Stmts   Miss  Cover   Missing
---------------------------------------------------
src/myapp/models.py      50     10    80%   12-18, 30-33
...

除外設定を適切に行うことで、本当にカバーすべきコードに集中したカバレッジ指標を得られるようになります。

CI環境(GitHub Actionsなど)でpytest-covを使う設定例

CI環境でpytest-covを使うと、プルリクエストごとに自動でカバレッジを計測し、閾値チェックを行うことができます。

ここではGitHub Actionsを例に、シンプルな設定を紹介します。

YAML
# .github/workflows/test.yml

name: CI

on:
  push:
    branches: [ main ]
  pull_request:

jobs:
  test:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout repository
        uses: actions/checkout@v4

      - name: Set up Python
        uses: actions/setup-python@v5
        with:
          python-version: "3.11"

      - name: Install dependencies
        run: |
          pip install -U pip
          pip install pytest pytest-cov
          # 必要に応じて: pip install -r requirements.txt

      - name: Run tests with coverage
        run: |
          pytest \
            --cov=src \
            --cov-report=xml \
            --cov-report=term \
            --cov-fail-under=80 \
            tests/

      - name: Upload coverage.xml as artifact
        uses: actions/upload-artifact@v4
        with:
          name: coverage-xml
          path: coverage.xml

このワークフローでは、次のことを行っています。

  1. pytestとpytest-covをインストール
  2. --covオプション付きでテストを実行
  3. XMLレポートを生成し、coverage.xmlとして出力
  4. カバレッジが80%未満の場合はジョブを失敗扱いにする
  5. coverage.xmlをアーティファクトとして保存する

さらに、CodecovやCoverallsといったサービスと連携する場合は、生成したcoverage.xmlを各サービスのActionに渡すだけで、リポジトリのREADMEにカバレッジバッジを表示することもできます。

toxやpytest.iniでpytest-cov設定を共通化する方法

毎回長いpytestコマンドを入力するのは手間ですし、開発者ごとに微妙に異なるオプションで実行されると、チームとしてのカバレッジ値が安定しない原因になります。

そのため、pytest-covの設定は設定ファイルで共通化しておくと便利です。

まず、pytest.iniでpytestの既定オプションを定義する例です。

INI
# pytest.ini

[pytest]
addopts =
    --cov=src
    --cov-report=term
    --cov-report=xml
    --cov-fail-under=80
testpaths =
    tests

この設定を入れておけば、開発者は単にpytestと実行するだけで、常に同じカバレッジ設定でテストを実行できます。

次に、toxを利用している場合の設定例です。

toxは複数のPythonバージョンや環境でテストを自動実行するツールです。

INI
# tox.ini

[tox]
envlist = py310, py311

[testenv]
deps =
    pytest
    pytest-cov
commands =
    pytest

pytest.iniにすでにpytest-covの設定を記述してある場合、tox側では単にpytestを呼び出すだけで、共通設定が適用されます。

最後に、.coveragercを組み合わせることで、カバレッジ関連の細かい除外設定も一元管理できます。

INI
# .coveragerc

[run]
source = src

[report]
omit =
    src/myapp/migrations/*
    */tests/*

このように、pytest.ini・tox.ini・.coveragercを適切に使い分けることで、個人環境・CI環境を問わず一貫したカバレッジ測定が可能になります。

まとめ

pytest-covを導入することで、pytestによるテストに「どこまでテストできているか」という可視化の力を簡単に追加できます。

ターミナル・HTML・XMLといった多様なレポート形式により、ローカル開発からCI連携まで一貫したカバレッジ管理が実現できます。

さらに、閾値設定や除外設定、pytest.iniやtoxによる共通化を組み合わせることで、チーム全体のテスト品質と効率を継続的に高めていけます。

まずは小さなプロジェクトで試し、徐々にプロダクションレベルのコードへ適用してみてください。

クラウドSSLサイトシールは安心の証です。

URLをコピーしました!