白盒テストは、ソフトウェアテストの典型的なシナリオの一つです。このシナリオでは、テスト担当者はプログラムのコードにアクセスし、コードの制御フローやデータフローなどの情報に基づいてテストケースを設計し、テストケースの実際の実行結果に基づいてプログラムが期待どおりであるか、バグが存在するか、テストケースの品質が高いかなどを判断することができます。
テストケースがテスト対象のプログラムを十分にテストしているかどうかを評価するために、コードカバレッジがよく使用されます。一般的に、テストケースができるだけ多くのコードを実行し、できるだけ多くのイベントをトリガーする場合、テストはより十分であると言えます。明らかなバグが発生しない場合、プログラムの品質は高くなり、実際のユーザーが入力する具体的なシナリオに対して安定性(ロバスト性)が高くなります。
プログラムのコードには通常、ループ、条件、関数呼び出しなどの特殊な構造が含まれています。これらの特殊な構造に対して異なるカバレッジルールを設計することで、さまざまなコードカバレッジ基準を派生させることができます。一般的なものには次のようなものがあります。
- ステートメントカバレッジ
- ブランチカバレッジ(判定カバレッジ)
- 条件カバレッジ
- パスカバレッジ
次のプログラムを例にします:
ステートメントカバレッジ#
ステートメントカバレッジ(ステートメントカバレッジ)は、実行されたコードの行数のみをカバレッジとして測定します。カバレッジの分母はテスト対象のプログラムのソースファイルの総行数であり、分子はプログラムの実行中に通過したコード行数です。たとえば、例のプログラムには合計で 10 行のコードがあり、1 行目と 10 行目は必ず実行されます。入力 a = 2, b = 3, c = 2
の場合、2 行目、3 行目、6 行目、7 行目が実行されるため、ステートメントカバレッジは 60%です。4 行目と 8 行目は排他的な関係にあるため、1 つの入力だけでは 100%のステートメントカバレッジを達成することはできません。次の 2 つの入力を作成することで、100%のステートメントカバレッジを達成できます:a = 2, b = 3, c = 4
および a = 2, b = 3, c = 1
。
ブランチカバレッジ(判定カバレッジ)#
ブランチカバレッジ(ブランチカバレッジ)または判定カバレッジは、各条件判定文のすべてのブランチがカバーされることを要求します。コードの論理をフローチャートで表現すると、ブランチカバレッジは直感的にはすべての接続線と矢印がカバーされることを意味します:
連続して実行される必要があるコードを 1 つのノードに統合すると、この図は次のように簡略化できます:
すべての接続線と矢印をカバーする必要がありますが、通常は条件文とループ文によって生成されるブランチのみを考慮します。たとえば、2 行目の条件文では、2->3
と 2->6
の 2 つのブランチをカバーする必要があります。4 行目の条件文では、4->5
と 4->6
の 2 つのブランチをカバーする必要があります。
例のプログラムには合計で 4 つの条件文があり、4 つの真理値を取ることができます。入力 a = 2, b = 3, c = 2
の場合、実行後の各条件文の判定値は T, F, T, F
であり、それぞれの分岐 2->3, 3->6, 6->7, 7->10
に対応します。分岐カバレッジ率を 100%にするには、判定値が F, T, F, T
になるような入力を作成するだけですが、例のプログラムでは、最初の条件が F になると、2 番目の条件が T になることはありません。次の 3 つの入力は、分岐カバレッジ率を 100%にすることができます:
a = 2, b = 3, c = 2 => T, F, T, F
a = 10, b = 3, c = 4 => T, T, F, _
a = 1, b = 1, c = 1 => F, _, T, T
すべての判定が T と F の両方を取るため、分岐カバレッジの要件を満たします。
条件カバレッジ#
判定は通常、複数の条件(ブール値を生成する最小の式)を組み合わせて作成されるため、制御フローの変化をより細かく評価するために、上記の分岐カバレッジの判定の概念を置き換えることができる条件の値を使用して条件カバレッジを構成することができます。例のプログラムでは、2 行目のコードについては、a > 1 and b > 2
の値が T、F の場合ではなく、a > 1
と b > 2
の値がそれぞれ T、F の場合を考慮する必要があります。条件カバレッジでは、各条件が T と F の両方で取られるだけで十分です。たとえば、1 つの入力が T、T を生成し、別の入力が F、F を生成する場合、条件カバレッジ率は 100%になります。分岐カバレッジで提供された 3 つの入力には次のようなものがあります:
a = 2, b = 3, c = 2 => T, F, T, F
a = 10, b = 3, c = 4 => T, T, F, _
a = 1, b = 1, c = 1 => F, _, T, T
a < 1
の場合が欠けているため、3 番目の入力を a = 0, b = 1, c = 1
に置き換えるだけです。
パスカバレッジ#
パスカバレッジは、最も細かいカバレッジであり、プログラムを入口(通常は main 関数)から出口(exit、panic、main 関数内の return 文)までの各ステートメントを組み合わせてパスとして扱います。少なくとも 1 つのステートメントが異なる場合、それは異なるパスと見なされます。例のプログラムには次のパスが存在します(見落としがないことを願っています):
1,2->3,4->5->6->7,8->9->10
1,2->3,4->5->6->7,8->10
1,2->3,4->5->6->10
1,2->3,4->6->7,8->9->10
1,2->3,4->6->7,8->10
1,2->3,4->6->10
1,2->6->7,8->9->10
1,2->6->7,8->10
1,2->6->10
注意する必要があるのは、これはプログラムコードから分析されたすべての可能なパスであるということですが、実際の状況ではいくつかのパスは成立しません。たとえば、3,4->5
と 7,8->9
は同じパスには存在しないため、例のプログラムでは 100%のパスカバレッジを達成することはできません。
パスカバレッジは、これらのカバレッジ測定方法の中で最も要求が高いものであり、パスカバレッジを満たすと、他のすべてのカバレッジ(実際には存在しない場合を除く)も必ず満たすことができます。ただし、実際のプログラムには数え切れないほどの条件文、ループ文、およびこれらの組み合わせが含まれているため、パスの数は条件文とループ文の数の増加とともに指数関数的に増加し、高いパスカバレッジ率を達成することは非常に困難です。プログラムのコードが与えられた場合、存在するパスの数を計算することはほぼ不可能です。したがって、パスカバレッジを計算する際には、比率ではなく数を数える方法を使用し、より多くの異なるパスをトリガーする入力がより高品質のテストケースと見なされます。