(この記事は、開発元Parasoft社 Blog 「The Two Big Traps of Code Coverage」2018年3月2日の翻訳記事です。)
コードカバレッジの測定は、私にとってずっと興味のあるテーマです。一方では、テスト中にどれだけのコードをカバーしているのか把握していないという組織をよく見かけます。これは本当に驚きです。かと思えば、もう一方には、カバレッジの数字を非常に重視するが、テストの質と有効性にはほとんど無頓着という組織があります。こういった組織は、むやみに100%のカバレッジというドラゴンを追いかけ、100%を達成できればソフトウェアの品質が良い、あるいは最高であるとさえ信じています。これは、控えめに言っても、何をどれだけテストしたかを知らないのと同じくらい危険です。実際には、誤った安心感を与える可能性があり、いっそう危険であるとも言えます。
コードカバレッジは、ソフトウェアの品質を評価するうえで有益な数字ですが、それは目的ではなく手段であることを忘れてはいけません。カバレッジのためにカバレッジを追求するわけではなく、ソフトウェアのテストを適切に行ったという指標となるからこそ、カバレッジを追求する必要があるのです。テストがそれ自体で意味をなさないかぎり、いくら多数のテストがあっても、ソフトウェアの品質が高いという意味にはなりません。重要な目標は、すべてのコードが実行されているだけでなく、テストされているのを確認することです。すべてを完全にテストするのに十分な時間と予算がない場合は、少なくとも、重要なものがすべてテストされていることの確認が必要です。
こう言うのは、カバレッジが低い場合は、おそらくテストが十分でないことを意味しますが、カバレッジが高いというだけでは、必ずしも品質の高さと相関するとはかぎらないからです。構図はもっと複雑です。
「ちょうどよい数のテスト」を含む、安定して保守性の高いテスト スイートを持ち、安心してソフトウェアをリリースできる「十分な」カバレッジを達成するという、ほどほどのところが一番であるのは明白です。しかし、以下で説明するような両極端のカバレッジの罠はありふれています。
カバレッジの罠#1:「カバレッジを把握していない」
私には、カバレッジを測定しないというのは不可解に思えます ―― カバレッジツールは安価で豊富に存在します。私の友人は、カバレッジの数値が高くないことを承知しているので、開発者やテスト担当者は貧弱な数値をマネジメント層に提示したがらないのだと言います。私はこれがありふれた例ではないことを願っています。
カバレッジを測定しようとしたときにチームが遭遇する現実的な問題の1つは、システムが複雑すぎることです。部品の上に部品を重ね、そのまた上に部品を重ねてアプリケーションを構築していると、カバレッジの計測をどこで行うべきかを理解するだけでも大変な作業になる可能性があります。アプリケーションのカバレッジを測定するのが難しいのであれば、アーキテクチャについて再検討すべきです。
この罠にはまるもう1つのシナリオは、さまざまなテスト実行の数値を集計する適切な方法がわからないため、多数のテストがあるにもかかわらず、実際のカバレッジを計測できないというケースです。手動テスト、機能テスト、ユニットテスト、エンドツーエンドテストを行っている場合は、単純に数字を加算することはできません。これらのテストがそれぞれ25%のカバレッジを達成しても、組み合わせると100%になることはまずありません。実際に調べてみると、100%よりも25%に近い可能性のほうが高いでしょう。
結論を言えば、意味のある方法でカバレッジを測定して合算する方法は確かに存在します。Parasoftの提供するレポートおよび分析ツールParasoft DTPで収集された膨大なデータを活用すれば、包括的な、集約されたコードカバレッジビューを実現できます。Parasoft製品のアプリケーションモニターは、テスト中にアプリケーションからカバレッジデータを直接収集し、その情報をParasoft DTPに送信できます。Parasoft DTPは、すべてのテストプラクティスの、すべてのテストチームが行ったすべてのテストのカバレッジデータを集計します。
情報量が非常に膨大になるのではと思われたかもしれませんが、それは正解です。DTPには、この大量のデータをナビゲートし、テスト作業をどこに集中させるべきかを決定するに役立つインタラクティブなダッシュボードがあります。次のダッシュボードの例を見てください。
複数のテストが同じコードをカバーしている場合、多重にカウントされることはありません。また、テストされていない部分をすばやく簡単に確認できます。これによって、アプリケーションのどの部分が十分にテストされ、どの部分がテストされていないかを示します。詳細については無料のホワイトペーパーをご覧ください。
そう、これでカバレッジを測定しない理由はなくなりました。
カバレッジの罠#2:「カバレッジがすべて」
カバレッジがすべてだと誤って思い込むのはよくあるケースです。カバレッジを測定できるようになると、マネージャーが「カバレッジを増やそう」と言うのは珍しいことではありません。最終的に、数値自体がテストよりも重要になります。おそらく最も適切なたとえ話は 、私がParasoftの創設者、アダム・コラワから聞いたものです。
「楽曲にとって意味のあるピアノの鍵を叩くのではなく、ピアノの鍵を100%カバーするようピアニストに要求するようなものです。楽曲を演奏すれば、意味のある数の鍵がカバーされるのです」
ここに問題の根があります。無意味なカバレッジは、無意味な音楽と同じです。カバレッジは、実際に意味のあるコードの使用を反映する必要があります。そうでなければ、ただの雑音にすぎません。
そして雑音について言えば……カバレッジが増加するにつれて、カバレッジを上げるためのコストも上がります。テストを作成するだけでなく、テストを保守する必要もあることを忘れてはいけません。テストの再利用と保守について考えていないのであれば、そもそもテストを作成する時間が無駄になるので、作成するべきではないかもしれません。テストスイートが大きくなるにつれ、予想を超えてノイズの量が増加します。テストが2倍になれば、ノイズは2倍からときには3倍になる場合があります。無意味なテストは、実際的なコンテキストに基づいていないため、テストを実行するたびに対処しなければならないので、適切なテストよりもノイズが多くなります。まさに技術的負債というものです。役に立たないテストは本当に危険です。
現在、特定の業界、例えば安全性が重要な業界では、100%のカバレッジが要求されます。しかし、そのような場合でも、コード行が実行されさえすれば意味のあるテストとして扱うのは簡単ですが、それは端的に言って真実ではありません。私が良いテストかどうかを判断する際に訊ねるのは、次の2つの基本的な質問です。
1)テストが失敗した場合、それは何を意味するか?
2)テストが合格した場合、それは何を意味するか?
理想どおりなら、テストが失敗した場合、何かが誤っていることが分かります。そして、本当に優れたテストであれば、問題を修正するための正しい方向も示してくれるでしょう。しかし実際には、テストが失敗したときも、なぜ失敗したのか誰にも分からず、誰も問題を再現できず、テストが無視されるというケースが多々あります。逆に、テストに合格した場合も、何がテストされたのかが分からなければなりません。それは、特定のフィーチャーや機能が適切に機能していることを意味するはずです。
これらの質問のどちらかに答えることができない場合は、おそらくテストに問題があります。どちらにも答えることができない場合は、おそらくそのテストは利益よりも害のほうが大きいでしょう。
この罠から抜け出す方法は、まずカバレッジの値自体が目標ではないことを理解することです。実際の目標は、有益な意味のあるテストを作成することです。これはもちろん時間がかかります。単純なコードのユニットテストを作成するのは簡単ですが、現実の複雑なアプリケーションでは、ユニットテストを作成する場合、スタブやモックを作成したり、フレームワークを使用する必要があります。これにはかなりの時間がかかる可能性がありますし、ちょっと間があけば、使用するAPIの細部などは忘れてしまいがちです。テストに真剣に取り組んだとしても、本当に良いテストを作成するのにかかる時間は、予想以上に長くなる可能性があります。
ParasoftのJava開発テストツールであるParasoft Jtestには、まさにこの問題に役立つ新しい技術があります。ユニットテストアシスタントは、モックとスタブを正しく取得するという面倒な作業を行います。ユニットテストアシスタントは、既存のテストを拡張してカバレッジを増やすのにも役立ちます。良いユニットテストの作成を支援するだけでなく、テストのカバレッジと品質を改善するための推奨事項も示してくれます。
カバレッジが重要であり、カバレッジの改善が有益な目標であることを理解いただけたでしょうか。単にパーセンテージを追求するのが重要なのではなく、安定して保守性が高く、意味のあるテストを書くのが重要であることを忘れないでください。
Parasoft Jtestについて
Java対応静的解析・単体テストツール Parasoft Jtest
Jtestは、テスト工数の大幅削減とセキュアで高品質なJavaシステムの開発を強力にサポートするJava対応テストツールです。1,000個以上のコーディング規約をもとにソースコードを静的に解析し、プログラムの問題点や処理フローに潜む検出困難なエラーを検出します。さらに、JUnitを用いた単体テストについて、作成、実行、テストカバレッジ分析、テスト資産の管理といった単体テストに係る作業をサポートし、単体テストの効率化を促進します。