pytest-cov で pytest 実行時にカバレッジを取得しよう!

記事タイトルとURLをコピーする

pytest-cov とは

github.com

pytest-cov とは、Python のテスティングフレームワークである pytest 実行時にカバレッジを取得するプラグインです。内部的にはカバレッジの取得に Coverage.py モジュールを使用しています。

カバレッジとは

開発したプログラムのテストにおいて、どの程度の範囲をテストにより実行したか(カバーしたか)を表す指標です。一般的には、カバー率が高ければ高いほど、開発したプログラムがしっかりとテストされていると言えます。但し、実施したテスト内容の質を担保するものではないので、あくまで 1 つの指標だと理解しておくと良いでしょう。

また、カバレッジには主に以下の 3 種があります。

C0 カバレッジ(Statement coverage: 命令網羅)

対象のコードのうち、テスト実行した命令文の割合を表す指標です。例えば以下のようなコードがあります。

def statement_coverage(flg):
    print("Hello")
    if flg:
        print("world!")

このコードを以下のテストコードにより実行します。

def test_statement_coverage():
    statement_coverage(False)

この場合、全 4 行の命令のうち、4行目の print("world!") は実行されませんので、C0 カバレッジは 3/4 = 75% ということになります。

C1 カバレッジ(Branch coverage: 分岐網羅)

対象のコードのうち、テスト実行した条件分岐の割合を表す指標です。例えば以下のようなコードがあります。

def statement_coverage(flg):
    print("Hello")
    if flg:
        print("true!")
    else:
        print("false!")

このコードを以下のテストコードにより実行します。

def test_statement_coverage():
    statement_coverage(False)

3 行目の if 文と 5 行目の else のうち、else 側が実行されますので、C1 カバレッジは 1/2 = 50% ということになります。

C2 カバレッジ(Condition coverage: 条件網羅)

対象のコードのうち、テスト実行した条件の網羅率を表す指標です。例えば以下のようなコードがあります。

def statement_coverage(flg1, flg2):
    print("Hello")
    if flg1 or flg2:
        print("true!")
    else:
        print("false!")

引数の flg1、flg2 の組み合わせは以下表の 4 種類です。C2 カバレッジを 100% にするには flg1、flg2 がそれぞれ True、False になるパターンを網羅する必要があります。 例えば No. 1 と No. 4 をテスト、または No. 2 と No. 3 をテスト といった具合です。
 なお、後者の場合は else 文内の命令 (print("false!")) が実行されません。C2 カバレッジを満たしていても、こういったケースがあるため必ずしも網羅的にテストできているとは限らない点に注意しましょう。

No. flg1 flg2
1 False False
2 True False
3 False True
4 True True

また、上記の No. 1 〜 No. 4 の組み合わせを全て網羅する指標を MCC カバレッジ (Multiple Condition Coverage: 複合条件網羅) と呼びます。

試してみる

実際のプロジェクトへの導入方法を確認します。

インストール

pip でインストールが可能です。

$ pip install pytest-cov

実行方法

pytest 実行時に --cov オプションをつけると C0 カバレッジが取得されます。

$ pytest --cov=<カバレッジ対象のディレクトリ>

先ほどの C0 カバレッジの例の場合、以下のような結果が標準出力されます。

---------- coverage: platform darwin, python 3.9.13-final-0 ----------
Name                               Stmts   Miss  Cover
------------------------------------------------------
coverage_sample.py                     4      1    75%
------------------------------------------------------
TOTAL                                  4      1    75%

Stmts は命令の数、Miss は実行できなかった命令の数、Cover は網羅率(この場合は (4 - 1) / 4 = 75%)です。


--cov-branch オプションで C1 カバレッジも取得されます。

$ pytest --cov=<カバレッジ対象のディレクトリ> --cov-branch

先ほどの C1 カバレッジの例の場合、以下のような結果が標準出力されます。

---------- coverage: platform darwin, python 3.9.13-final-0 ----------
Name                               Stmts   Miss Branch BrPart  Cover
--------------------------------------------------------------------
coverage_sample.py                     5      1      2      1    71%
--------------------------------------------------------------------
TOTAL                                  5      1      2      1    71%

Branch は条件分岐の数、BrPart は実行されなかった条件分岐の数です。また、この場合 Cover は C0 と C1 を合わせた網羅率 ( ((5 + 2) - (1 + 1)) / (5 + 2) = 71% ) となります。


C2 カバレッジについては現状サポートされていません。テストコードで条件分岐を確実に網羅出来ているか、コードレビューなども含めチェックしていきましょう。
必要に応じてディシジョンテーブルテスト等も検討すると良いと思います。


--cov-report オプションで出力形式(xmlhtml、デフォルトは term です)を選択できます。

$ pytest --cov=<カバレッジ対象のディレクトリ> --cov-report=<出力形式>

html の場合、htmlcov ディレクトリに結果が出力されます。 以下のようにビジュアルで結果が確認可能です。

その他のオプションについてはドキュメントを参照ください。

pytest-cov.readthedocs.io

Visual Studio Code で可視化したい

結果レポートは html で確認可能ではあるものの、テスト実行 -> 結果 html 確認 -> テスト修正 ... を繰り返す場合、ブラウザとエディタを行ったり来たりするのは面倒です。
そこで、結果レポートを Visual Studio Code(以降 VSCode) 上で可視化する方法もご紹介します。
以下のようなイメージです。緑色がテスト実行済み、黄色が分岐の一部のみ実行済み、赤が未実行を表しています。


VSCode 上での可視化は、Coverage Gutters という拡張機能で実現可能です。

marketplace.visualstudio.com

pytest 実行時、--cov-report オプションに xml を指定すると coverage.xml が出力されます。
出力後、VSCode の画面下部のバーにある ○Watch をクリックすると Coverage Gutters が coverage.xml を読み込んでカバレッジを表示してくれます。

まとめ

pytest-cov を使ったカバレッジの取得方法についてご紹介しました。カバレッジはあくまで指標の一つですが、継続的にチェックしてテスト品質を高めていきましょう!

あわせて読みたい

blog.serverworks.co.jp

blog.serverworks.co.jp