そもそもカバレッジって一体何なの
コードカバレッジは、コードのテストされていない部分を発見するための有用なツールである。
テストカバレッジ
コードを書いた、それからとりあえず動作確認するときに気にするといいですね。
コードの中に動かしてない箇所がある、何それ怖い。だからテストを実行してカバレッジを取得して、動かしてない部分があるか確認する。
そういう使い方が正しいと、私は考えています。
あとはリファクタリングや改修の後の確認のひとつとして。
通るはずの行が通らない/通らないはずの行が通る、そんな箇所では修正ミスをしているかもしれません。
素敵なツールですね。積極的に使っていきたいです!
カバレッジ100%であるコードの正確性
ですが、こんなことも言われています。なぜでしょうか。
ただテスト自体がどれだけ良いかという指標としては、テストカバレッジはほとんど役に立たない。
テストカバレッジ
カバレッジは、その行を通ったか/通っていないかだけを教えてくれます。
その行を通ることが正しい/正しくない、それは検証しません。
あれ?ということは、処理内容の正しさの確認としては無意味ということ……?
なぜ目的にしてはいけないのか
カバレッジの数字ばっかり気にして、自分が何をやっているかわかっていない人間のいる臭いがする。
テストカバレッジ
カバレッジ100%を目的にした場合、これこそが陥りやすい罠です。
以前、単体テスト完了とする基準が「JUnit実行してカバレッジ100%」であるプロジェクトに遭遇したことがあります。
結合テストやってるけど、品質がやばくて単体テストからやり直ししたいからヘルプしてどうぞって指示で見てみたところ、テストコードはJUnitテストクラスなだけで、テストの意味がなかったんです。
カバレッジは、ステートメントカバレッジ(C0:命令網羅率)でいえばほぼ100%でした。
(判断文カバレッジ(C1:分岐網羅率)はいいとしても、単純条件カバレッジ(C2:条件網羅率)は随分と漏れがあったけど1)
その上で、実行結果に対する検証は一切ありません。
──つまり、テスト対象を呼び出しただけ。だって目的がカバレッジ100%だから。
その結果、処理結果に対する正確性の確認が、丸ごと漏れていました。
具体的に再現例を出しましょう。恐れ慄くといい。
// 空文字チェック
public static boolean isEmpty(String value) {
if (value.length() != 0) {
return true;
}
return false;
}
// 空文字チェックのテスト
@Test
public void test_isEmpty() {
Sample.isEmpty("");
Sample.isEmpty("ABC");
}
上記は、確かにカバレッジが100%になります。
戻り値がtrue/falseのどちらでその値が意図通りなのかどうかが未検証ですが、基準が前述の通りでしたのでテスト完了!としていました。
……ひどい。この世の地獄か。……まあ実際地獄でしたよテストクラスの修正は。
実際にあった本当に怖い話、です。
※再現例について
上記で不足しているのは、処理結果の検証だけではありません。
テストケース(データのバリエーション)もですが、そちらは別の話なので割愛しています。
再現例では例えば、引数がnullだとNullPointerExceptionが発生します。テストは失敗です。
NullPointerExceptionが発生するのが仕様として正しいのかどうかは、別問題ですが。
また、テストを実行しても引数がnullの場合の検証がありませんので、その仕様が正しいかどうかの検証もされません。
引数がnullの場合の仕様が設計から漏れているのか、それとも単に実装者の考慮漏れなのかもわかりませんね。
ただ、カバレッジ100%が目的のテストでは、品質が保証できないことだけは確実です。
何のためにテストを行うのか
どうしてテストを行うのか。
それはカバレッジを100%にするためではなく、コードが仕様通り動くことを確認するためです。
テスト自体の品質評価として、カバレッジだけでは役に立ちません。
カバレッジが100%にするためのテストは、いくら実施しても品質向上に寄与しません。
ですので、カバレッジ100%を目的とするのはやめましょう。
個人的には、「カバレッジ100%」は、品質向上のための手段のひとつとしてなら、いいと思うんですけどね。
自分の書いたコード内に動かしてないところが存在するなんて不安ですし。
手段ではなく目的にしてしまうケースがとても多いので、いろんなところで否定されているのだと思います。
-
カバレッジの種類について カバレッジ(網羅率)分析とは ↩