テストカバレッジの読み方
テストカバレッジとは、テストを動かしたとき、プログラムのどれだけが実際に実行されたかを表す割合です。一度も動いていない行が、見つかります。
「カバレッジ80%」と言われたら、残りの20%は誰も確かめていません。その20%に、バグが隠れているかもしれません。
このレッスンのゴール
メモ
カバレッジの数字を読んで、それが見逃すものを説明することができる。
キーワード
| ことば | いみ | れい |
|---|---|---|
| カバレッジ | テストが動かしたコードの割合 | 80% |
| 行カバレッジ | 実行された行の数を、全部の行の数で割った割合 | |
| 分岐カバレッジ | if の真と偽の、両方を通ったかを数える割合 | |
| トレース | 実行された行を順に残した記録 | |
| 未実行の行 | テストの間に、一度も動かなかった行 |
学生が入力するセルは、このプレビューでは空白です。
カバレッジはどの行が動いたかの割合
テストを動かしながら、どの行が実行されたかを記録します。実行された行の数を、全部の行の数で割ったものが行カバレッジです。
下の表は、成績を A、B、C に分ける関数を、5つの部分に分けたものです。90点と60点の2つのテストでは、1つの部分が動きません。
例: 2つのテストで動いた部分
| 印 | 部分の内容 | 90点で動く | 60点で動く |
|---|---|---|---|
| 1 | 最初の if を調べる | 動く | 動く |
| 2 | "A" を返す | 動く | 動かない |
| 3 | 次の if を調べる | 動かない | 動く |
| 4 | "B" を返す | 動かない | 動く |
| 5 | "C" を返す | 動かない | 動かない |
学生が入力するセルは、このプレビューでは空白です。
動いた印を数えるコード
ran = []
def grade(score):
ran.append(1)
if score >= 80:
ran.append(2)
return "A"
ran.append(3)
if score >= 50:
ran.append(4)
return "B"
ran.append(5)
return "C"
for score in [90, 60]:
grade(score)
done = []
for mark in ran:
if mark not in done:
done.append(mark)
print("動いた印:", done)
print("カバレッジ:", len(done) * 100 // 5, "%")
# リストに 10 を足して、100% になるか確かめましょう。
Python の trace で未実行の行を探す
Python には、実行された行を数える trace モジュールが付いています。--missing を付けると、一度も動かなかった行の頭に >>>>>> が付きます。
ラボでは、成績の関数に 90 と 60 の2つだけを渡して動かします。次に、10 も渡して、印が消えるかを確かめます。
作業用のフォルダを作る
mkdir -p ~/testing/coverageここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
そのフォルダへ移る
cd ~/testing/coverageここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
2つの点数で動かす版を書く
printf 'def grade(score):\n if score >= 80:\n return "A"\n if score >= 50:\n return "B"\n return "C"\n\n\nprint(grade(90))\nprint(grade(60))\n' > grade.pyここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
trace を付けて動かす
python3 -m trace --count --missing --coverdir=. grade.pyここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
まず A と B が表示される
プログラム自身の出力として、A と B が表示されます。同時に、grade.cover というファイルが作られます。
次のコマンドは、その中から >>>>>> の付いた行だけを出します。
未実行の行を探す
grep '>>>>>>' grade.coverここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
出るのは return "C" の1行
出力は、行頭が >>>>>> で、そのあとに return "C" が続く1行です。90点でも60点でもこの行には届かず、一度も動いていません。
今度は、10点も試す版を書いて、同じ調べ方をします。
3つの点数で動かす版を書く
printf 'def grade(score):\n if score >= 80:\n return "A"\n if score >= 50:\n return "B"\n return "C"\n\n\nprint(grade(90))\nprint(grade(60))\nprint(grade(10))\n' > grade_all.pyここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
もう一度 trace で動かす
python3 -m trace --count --missing --coverdir=. grade_all.pyここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
未実行の行の数を数える
grep -c '>>>>>>' grade_all.coverここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
数えると 0 になる
A、B、C が表示されたあと、最後のコマンドは 0 と表示します。未実行の行が0本で、行カバレッジが100%になったからです。
行が全部動いても分岐は残る
if には、条件が真の道と偽の道があります。分岐カバレッジは、その両方を通ったかを数えます。
会員なら100円引く関数を、会員の場合だけ試すと、すべての行が動きます。行カバレッジは100%です。しかし、会員でない道は一度も試されていません。
例: 行と分岐のカバレッジ
| テスト | 動いた行 | 行カバレッジ | 通った分岐 |
|---|---|---|---|
| 会員だけ | 4行すべて | 100% | 2つのうち1つ |
| 会員と非会員 | 4行すべて | 100% | 2つのうち2つ |
学生が入力するセルは、このプレビューでは空白です。
会員だけを試したコード
def discount(price, member):
if member:
price = price - 100
return price
# 会員の場合だけ試しました。すべての行が動きます。
print(discount(1000, True))
# 次の行の # を外して、会員でない場合も試しましょう。
# print(discount(1000, False))
100% でも正しいとは言えない
カバレッジは、コードが動いたことしか言いません。結果が正しいかは、確認の行(アサーション)が決めます。
何も確かめないテストでも、関数を動かせばカバレッジは上がります。数字は目標ではなく、見落としを探す地図として使いましょう。
動かしても確かめないテスト
def total(price, count):
return price + count
# このテストは関数を動かしますが、何も確かめません。
total(120, 3)
print("エラーなしで終わりました")
# 次の行の # を外すと、確かめる1行が働いて、止まります。
# assert total(120, 3) == 360
よくある間違い
注意
「カバレッジが100%なら、バグはない」と考える人が多くいます。100%は、すべての行が動いたことを言うだけです。結果を確かめていなければ、間違った答えのまま通ります。