pythonのカバレッジをpytest-covで調べる

# はじめに
[以前](https://qiita.com/mink0212/items/40e4f796eb4ba7868c08)にpythonのpytestを使用してflaskの単体テストをする方法をまとめました。
単体テストには処理ルートや分岐条件が網羅されているかという観点のテスト(ガバレッジテスト)が良くされています。その網羅率がどれぐらいかを判定してくれる``` pytest-cov ```というライブラリの使用方法をまとめます。

# 環境
 - python:3.6.5
 - flask:1.0.2
 - pytest:5.3.5
 - pytest-cov:2.8.1

# インストール
 - ```pip install pytest```でpytestをインストールする。
 - ```pip install pytest-cov```でpytest-covをインストールする。

# pytest-covでガバレッジをみるのに必要なもの
pytest-covはpytestを元に動くためpytestが実行できる環境(テスト対象のソースとテストソース)が必要になります。pytest-cov用に必要なものは何もありません。pytestの使い方については[pytestでflaskの単体テストをする](https://qiita.com/mink0212/items/40e4f796eb4ba7868c08)を見てください。

# 簡単な関数の単体テストのガバレッジ確認
[単体テストについてまとめたとき](https://qiita.com/mink0212/items/40e4f796eb4ba7868c08)と同様に簡単な関数を通じてpytest-covの使い方を見ていきます。

## テスト対象のソース
Branchのチェックのために[単体テストについてまとめたとき](https://qiita.com/mink0212/items/40e4f796eb4ba7868c08)の例にif文をつけたソースを使用します。

``` testing_mod.py
def add_calc(a, b):
    if (a == 1):
        b = b + 1
    return a + b

```

## テスト方法を書いたソース
[単体テストについてまとめたとき](https://qiita.com/mink0212/items/40e4f796eb4ba7868c08)と同じです。

``` py_test_main.py

import pytest
import testing_mod

def test_ok_sample():
    result = testing_mod.add_calc(1, 2)
    assert 4 == result

```

## 単体テストの実行
テスト対象とテスト方法のソースができたため実行します。処理ルートの網羅か条件分岐の網羅どちらのテストをするかにより、実行方法が若干異なります。

### 処理ルートの網羅の単体テスト実行
pytestを実行するときに``` --cov ```オプションを追加することで処理ルートの網羅テスト率(C0カバレッジ)を見ることができます。

```
# pytest --cov -v py_test_main.py

～～～～　略　～～～～～

py_test_main.py::test_ok_sample PASSED                        [100%] 

----------- coverage: platform win32, python 3.6.5-final-0 -----------
Name              Stmts   Miss  Cover
-------------------------------------
py_test_main.py       5      0   100%
testing_mod.py        4      0   100%
-------------------------------------
TOTAL                 9      0   100%

====== 1 passed in 0.05s ======
```

結果を見るとテスト対象のtesting_mod.pyのCoverが100%となり、全処理ルートが通ったことがわかります。ちなみにStmtsとはテスト時に通った処理の行数でMissはテスト時に通らなかった処理の行数になります。

### 条件分岐の網羅の単体テスト実行
pytestを実行するときに``` --cov --cov-branch ```オプションを追加することで条件分岐の網羅テスト率(C1カバレッジ)を見ることができます。

```
# pytest --cov --cov-branch -v py_test_main.py

～～～～　略　～～～～～

py_test_main.py::test_ok_sample PASSED                           [100%] 

----------- coverage: platform win32, python 3.6.5-final-0 -----------
Name              Stmts   Miss Branch BrPart  Cover
---------------------------------------------------
py_test_main.py       5      0      0      0   100%
testing_mod.py        4      0      2      1    83%
---------------------------------------------------
TOTAL                 9      0      2      1    91%

====== 1 passed in 0.08s ======
```

結果を見ると先ほどとは異なりBranchとBrPartが増えています。今回はif文に入る条件しかテストをしていないためtesting_mod.pyが83%になっています。ちなみにBranchは条件分岐の数、BrPartは通っていない条件の数になります。
今回の例だとBranchはif文に入る条件とif文に入らない条件の2パターンであり、if文に入らない条件のテストをしていないためBrPartが1パターン出ていることになります。

#### テストソースの追加
branchを100%にするためにテストを追加します。

``` py_test_main.py

import pytest
import testing_mod

def test_ok_sample():
    result = testing_mod.add_calc(1, 2)
    assert 4 == result

def test_ok_not_if_sample():
    result = testing_mod.add_calc(2, 2)
    assert 4 == result
```

#### 実行
branchが100%になっているか確認するために再度実行してみます。

```
# pytest --cov --cov-branch -v py_test_main.py

～～～～　略　～～～～～

py_test_main.py::test_ok_sample PASSED                             [ 50%] 
py_test_main.py::test_ok_not_if_sample PASSED                      [100%] 

----------- coverage: platform win32, python 3.6.5-final-0 -----------
Name              Stmts   Miss Branch BrPart  Cover
---------------------------------------------------
py_test_main.py       8      0      0      0   100%
testing_mod.py        4      0      2      0   100%
---------------------------------------------------
TOTAL                12      0      2      0   100%

====== 1 passed in 0.08s ======
```

結果を見るとちゃんとCoverが100%、BrPartが0になっていました。

# おわりに
カバレッジはとても分かりやすく機械的に考えられるのでバグを減らす良い観点です。
ただし、人によってはカバレッジを絶対のものと考えてしまいテストの漏れやテストの工数が無駄に増える事態を引き起こしてしまいます。例えば、「条件1と次の条件2の組み合わせがおかしくバグを見逃したけどカバレッジがOKでした」であったり、「DBの接続エラーにより発生するエクセプション作成処理(全SQL分)を通すために1週間かかりました」などがありました。
一方で今回の例のようにif文に入らない条件など見落としがちな条件も見つけられるため、とても有効なライブラリになります。
個人的には、まず一回カバレッジを見てみて条件を分析した後にテストの有無を考えるような補助ツールとして使うのが良いのではないかと思っています。