ソフトウェアの品質を学びまくる

ソフトウェアの品質、ソフトウェアテストなどについて学んだことを記録するブログです。

コードカバレッジについて、また考える - その1

 JSTQB Advancedの受験勉強中、出題範囲とはまったく関係ないコードカバレッジにはまって時間を浪費したので、整理することにしました。実業務で何の役に立つかという話もありますが、考え方として、勉強になると信じる。

まず最初に

対訳について

 英語と日本語の対応を明確にしておきます。いくつかの対訳がありますが、このエントリでは以下のような対応とします。

  • statement = 命令: コメントでも空行でもない、コードの行
  • condition = 条件: 真偽の2値で決まる論理式。論理演算子は含まない
  • decision = 判定: 条件(やその組み合わせ)の評価
  • determination = 決定: 後ほど言及します

ざっくり概要

 コードカバレッジと呼ばれるもののうち、今回書くのは以下の8つです。
 同義語がたくさんあるので注意してください。用語集を調べるのもよいですが、たこはちさんのサイトで簡単に検索もできます。

  1. 命令網羅(Statement Coverage)
  2. 判定網羅(Decision Coverage)
  3. 条件網羅(Condition Coverage)
  4. 条件判定網羅(Condition Decision Coverage)
  5. 複合条件網羅(Multiple Condition Coverage)
  6. 条件判定網羅・改(Modified Condition Decision Coverage, MCDC)
  7. LCSAJ(Linear Code Sequence And Jump)
  8. 経路網羅(Path Coverage)

 流れとしては、一番単純な(1)。ちょっと工夫して(2)と(3)を考え、その2つを組み合わせたのが(4)。(4)の弱点を補おうと思い切って(5)を考えたものの、これは大変なのでちょっと気を緩めた(6)に至る。(1)~(6)では、コードが上から下に流れていたのに対し、(7)はループによる戻りも考慮されている。(8)はまさに最強。てなところでしょうか。

ところでbranchって?*1

 本論に入る前に、分岐(branch)について。
 判定網羅(decision coverage)と分岐網羅(branch coverage)は、同一のものとして扱われていることが多いように思いますが、シラバスでは

デシジョンテストとブランチテストは、カバレッジ100%の場合同じになるが、カバレッジレベルが低い場合は異なることがある

とあります。明確に、別のものなんですね。

 分岐網羅が分岐から出てくる「線」をどれだけ網羅しているかを見ているのに対し、判定網羅はフローチャートの菱形から矢印の出てくる「点」をどれだけ網羅しているかフローチャート全体の「線」をどれだけ網羅しているかを考えるようです。

20110925_01

 右上の適当フローチャートでいうと、菱形から線を生み出す点出てくる線は2つ。なので分岐網羅を計算するときの分母は2です。
 一方全体としては、菱形から2本上下3本、長方形からも下にも1本出ていて、合計34本。菱形での判定が下に行くなら、3本中2本4本中3本が網羅され、分岐網羅率は66.6%75%、ということになるようです。多分!

tacohachiさんからいただいたコメント

 2011年10月1日に、以下のコメントをいただきましたので掲載させていただきます。

branchとdecisionの違いは、decisionはdecision pointとなる条件を扱いますが、branchは、単にsequenceからの枝分れを扱います。if, switch, while, untilなどの条件jumpを伴うステートメントでは両者は同じです。決定的に違うのは、branchには無条件jumpを含むという点です。つまり、goto, continue, break、サブルーチンの呼び出しなどもbranchとみなすということです。と、私は理解しています。
(中略)
シーケンスから分岐にはgotoを補うと分かりやすいようです。
(a=240,b=120)のときは、branch 3, 6, 7を通過し、3/7 = 43%ということのようです。

if (a == 245){
// branch 1
write("bingo"); 
goto L1; // branch 2
} else {
// branch 3
;
}
L1: if (a < b){
// branch 4
a = b;
goto L2; // branch 5
} else {
// branch 6
a = 0;
goto L2; // branch 7
}
L2: write(a);

具体例で考えてみる

 さて本論。以下のようなプログラムを考えてみましょう。

映画チケットは1,800円。 ただし、水曜日で、かつ女性のお客様であれば、1,000円に割引。

 ここで、「水曜日である」と「女性である」がそれぞれ条件で、その条件の組み合わせを評価した結果が判定。その判定結果が真であれば、「チケット1,000円の割引を適用する」という命令を実行すると考えます。
 では、(1)~(6)の各カバレッジを100%にするために、テストケースはどのような組み合わせになるかを、下の表*2に示します。

テストケース 条件 判定 間違った判定 各カバレッジでの選択
# 曜日 性別 水曜である 女性である 水曜 かつ 女性 水曜 または 女性 1 2 3 4 5 6
A 水曜 女性
B 水曜 男性
C 月曜 男性
D 月曜 女性

 たとえば(3)条件網羅であれば、「3」で○のついているテストケース#Bと#Cを行えば、網羅率が100%になる、という意味です*3

(1)命令網羅

 「割引を適用する」という命令さえ実行されればよい。よってテストケース#Aのみでカバレッジ100%になります。

(2)判定網羅

 命令網羅だと、他のケースで間違って割引されないか、不安ですね。判定が「偽」になるケースも考えておきましょう。テストケース#Aに加えて#Bもやっておけば、割引されないケースも確認できます。判定が真の場合、偽の場合、両方が確かめられるわけです。
 ただこれだと、「水曜日でない」というテストケースが出てこないことになりますね。条件の真偽が網羅できていないのです。

(3)条件網羅

 条件の真偽を網羅するのであれば、#Bと#Cを選びましょう。曜日が「水曜日である」「水曜日でない」、性別が「女性である」「女性でない」の、すべての条件を確認できます。
 しかし今度は・・・どちらのケースでも割引が適用されません。つまり判定が網羅できていないというわけです。

(4)条件判定網羅

 #Aと#Dだとどうでしょう。「水曜である」「女性である」の条件の真偽も、「割引対象である」という判定も網羅されますね。

(5)複合条件網羅

 ここで疑心暗鬼な人が現れます。
 チケット売り場の人が、割引条件を「水曜日、かつ女性」じゃなく、「水曜日、または女性」と勘違いしていたらどうだろう。#Aと#Dのケースはそれぞれやっぱり、「割引対象である」「対象でない」と「正しく」判定してしまうではないか。つまり、条件判定網羅だと、条件を結びつける演算子のミスを見逃すかも知れない・・・。

 こうなると、#A~#Dの全真偽パターンやるしかないのか・・・?ということになってしまいます。今回は条件が2つなので4パターンですが、条件がn個あれば、2n個のテストケースに・・・。

(6)条件判定網羅・改

 ここでちょっと工夫をしてみます。問題になっている演算子はAND。よって、最初に評価した条件(「水曜日である」)が偽なら、もうその時点で全体の判定も偽になります。
 つまり、「水曜でない」なら、「女性である」が真だろうが偽だろうが判定には影響しません。だからどちらかをテストすればいいということになります。今回の例では、#Cはやらなくても、判定網羅、条件網羅、演算子誤りの検出が可能だということです。
 逆に演算子がORの場合、最初に評価する条件が真になれば以降の条件は判定に影響しませんね。

条件判定網羅・改、MCDCについて

言葉の定義

 ソフトウェアテスト標準用語集では、MCDCを次のように定義しています。

判定結果に対し、独立に影響する全単一条件をテストスイートが実行したパーセンテージ。

 この「独立に影響する」というのはどういう意味でしょう。
 「水曜でない」と決まったら、「女性である」でも「女性でない」でももう関係ない。つまり「女性である」は、判定結果に独立に影響していません。
 一方「水曜日である」ならば、「女性である」「女性でない」は、それぞれ別の結果をもたらすので、「水曜日である」と組み合わせることで独立したケースとなり、テストを行う必要があるということになります。

MCDCをどう訳すか

 「条件判定網羅・改」というのは勝手につけた名前で、英語は「modified」。シラバス用語集では「変更条件判定カバレッジ」と訳していますが、「変更」はちょっと違うんじゃないのかなあと。「改善型」「改良型」が、わかりやすいかなと思っています。
 また、MCDCには別に、「condition determination coverage」という名前もあります。たこはちさんの用語集ではこの「determination」を「判定」と訳し、「decision」の方は「デシジョン」としています。が、わたしはこの「determination」を「決定」と訳したいと思います。理由は以下2点。

  1. カバレッジの話では、「判定」は「decision」の訳が浸透している
  2. MCDCにおけるテストケースの減らし方は、決定表の圧縮と同様であり、「決定」の言葉が馴染む

 ただ、決定表の英訳って「decision table」なんですけどね・・・。

テストケースの減らし方

 機械的な減らし方は、コチラの論文(PDF)に書かれています。このエントリに書かれたいい加減な感じの内容をしっかり知りたい人は、この論文を読むのがよいと思います。

 ところで、(5)複合条件網羅の弱点は、n個の条件に対してテストケースが2^nになってしまうことだけではありません。 たとえばJavaで、論理積演算子「&&」はshort-circuitなので、最初のオペランド「水曜日である」が偽になったら、「女性である」はそもそも評価されません。つまり、実装によっては実現の困難なテストケースが出てくるというわけですね。
 よってテストケースを減らす手順は決定表の圧縮の考え方と同様、条件が評価される順番を考慮する必要があります。

 その2で、LCSAJ(何とかならんか、この名前)とパスカバレッジについて述べます。

www.kzsuzuki.com

追記(2018/9/30): C0、C1、C2について

 組織によっては、命令網羅・判定網羅・条件網羅をそれぞれ「C0」「C1」「C2」を呼んでいることもあります。この略称には、少なくとも2つの課題があります。情報源が秋山さん・居駒さんツイートの引用で、原典未確認の手抜きですが・・・。

略称と定義が異なることがある

 だそうです。同じ言葉の定義が人によって違う、というのはコミュニケーションの敵です。特にこの条件を、発注の契約条件などにしてしまうと・・・。

C2はC1を包含していない

 0→1→2と上がるごとにカバレッジも上昇しそうなので、判定網羅と条件網羅は包含関係にありません。
 条件網羅は、条件の真偽さえ網羅していればいいので、判定結果を考慮していない。上の表でも、「水曜かつ女性」が真になるケースは出てこないので、判定網羅を包含していないことになります。

参考文献

*1:この箇所は、秋山さんのツイートを見て修正しました。

*2:表の一部に誤記がありました。赤字で修正しています。鹿せんべいさん、ご指摘ありがとうございました。

*3:あくまでイメージです。たとえば判定網羅は#A・#Bでなく、#A・#Dでもよい。ただ、例が単純なので、#1・#4は判定網羅だけでなく、条件判定網羅も100%になったりします。