
第220回: 「ソフトウェアテストしようぜ」34 CEGTest(4. ロジックを網羅する 中編)
◀前の記事へ 次の記事へ▶
≡ はじめに
前回は、「ロジックを網羅する」の前編でした。前編は単純な「NOT」を扱いました。
今回は、残りのANDとORのうちANDが網羅するものについて確認していきます。原因結果グラフの肝の部分です。今回と次回の話が腹落ちすると、原因結果グラフが使いたくなります。
なお、ORについては、次回となりますが、今回の内容が理解できたかどうかについて、今回の内容からORの場合の説明が思いつくかどうかでわかりますので、最後まで読んだら、ORについて考えてみてください。
≡ ANDとOR
私たちは、日々、何かの条件(事象)をもとに意思決定をして行動しています。
ここで、「条件」を”condition”(コンディション)、「意思決定」を”decision”(デシジョン)と言います。
デシジョンテーブルは、条件の組み合わせによって意思決定する規則を列で表現しています。
例えば、「雨が降っている」という【事象】(条件)を見て意思決定をして、「傘を差す」という【行動】をとります。
この意思決定の規則を原因結果グラフのCEGTestツールに入力してみます。

テストでは、デシジョンテーブルのルール1(#1)とルール2(#2)を実施します。つまり、#1から「雨が降っている(が真(True))のときは、傘を差す」というテストを実施し、#2から「雨が降っていない(=”雨が降っている”が偽(False))のときは、傘を差さない(=”傘を差す”が偽(False))」という2つのテストを実施します。
こちらで全てのロジックを網羅しています。
この例のように、原因と結果の論理値(TとF)が同じ関係を実現する関数をIdentity(同値関数)と言います。
ちなみに原因がTのときに結果がFとなる反対の関係を実現する関数をNot(否定関数)と言います。そして、今回のAndは積関数、次回のOrは和関数と呼びます。
ここまでは、前回の復習でした。
ここから、もう少し複雑な条件について考えてみます。それは、複数の条件の組合せです。
■ ANDのケース
例題:
「(天気予報によると)夜に雨が降る確率が高い」、かつ、「外出の帰りは夜になる予定」の場合は、「傘を持って出かける」という意思決定について考えます。
この意思決定のデシジョンテーブルを作成しなさい。
例題中の「かつ」という部分を「AND」と呼びます。「夜に雨が降る確率が高い」と、「外出の帰りは夜になる予定」の2つの条件が両方とも成立するときに「傘を持って出かける」という行動をとります。
こちらについて、原因結果グラフを描いてみます。

「夜に雨が降る確率が高い」と、「外出の帰りは夜になる予定」の2つの条件(原因)を「傘を持って出かける」という結果に対して、ANDで結んでいます。
2つの原因をANDで結んだことは、結果ノードの先頭にあるマークがANDを表す論理記号である「∧」であることからわかります。
CEGTestが出力したデシジョンテーブルを確認します。

ここで、原因の組合せについて「T/T」、「F/T」、「T/F」の3通りしかない点に着目してください。「F/F」の組合せがありません。
#1から #3の列(=ルール)がそれぞれ、何を確認(網羅)しているかを黄色の行で追加します。

この表を理解することが、原因結果グラフがわかったといえる最大のポイントです。そこで、ゆっくりと説明します。
まず、このデシジョンテーブルは以下の例題の意思決定規則を表しています。
「(天気予報によると)夜に雨が降る確率が高い」、かつ、「外出の帰りは夜になる予定」の場合は、「傘を持って出かける」という意思決定
例題には、「夜に雨が降る確率が高い」(条件1)と、「外出の帰りは夜になる予定」(条件2)の2つの条件があり、それのANDを取った結果にしたがって、「傘を持って出かける」かどうかの意思決定をおこなうものです。
条件1 AND 条件2 = 結果
ということです。
さて、デシジョンテーブルを再掲します。

ルール1(#1列)は、結果が真になるケースです。条件1と条件2がANDで結ばれていますので、結果が真になるためには、条件1と条件2の両方が真(T)になる必要があります。
「両方が真」ということは、ルール1(#1列)は、「条件1が真(T)であることが結果に反映している」ことのテストとなります。条件2も同じです。
最後の行に書いたとおり、ルール1(#1列)は、「条件1が真(True)」と「条件2が真(True)」の2つのロジックを確認(網羅)しています。
ルール2(#2列)は、「条件1が偽なので結果が偽になるケース」です。ルール2ではこの1つのロジックしか確認していません。条件2が真だから結果が偽になっているわけではありません。
「条件1が偽で結果が偽になるケース」には、ルール2の「条件1がFで、条件2がT(T/T)」の組合せのほかに「条件1がFで、条件2もF(F/F)」の組合せもあります。
ところが、「条件1が偽なので結果が偽になるケース」というと、ルール2の「F/T」の組合せの1択となります。その理由は、両方とも偽の場合、「条件2が偽だから結果が偽になった」という可能性があるからです。そこで、「条件2については必ず真をセットしておき、条件1のみを偽にする」ことによって、結果が偽になったのは、条件1が偽だからだと確実に言えるようになります。
このことを文章で確認します。ルール2は、「夜に雨が降る確率が高い」が偽で、「外出の帰りは夜になる予定」が真です。
結果の「傘を持って出かける」はANDの結果、偽です。
「夜に雨が降る確率が高くなければ、たとえ、外出の帰りが夜になる予定だったとしても、傘を持って出かけることはない」という意思決定です。
さて、こちらの結果ですが、条件が2つの場合はF/Fの1項目減るだけなので、あまり有難味を感じないと思います。
ところが、条件の数が増えると大きな効果となって現れます。
以下は条件が5つの場合の原因結果グラフとデシジョンテーブルです。

ANDの場合、全部の条件を真にしたテスト(#1)と、各条件を1つずつ偽にしたテスト(#2~#6)となります。
条件の数が5つということは全組み合わせは2⁵ = 32です。
ところが原因結果グラフを用いれば、32回のテストが、約1/5の6回になります。
言い換えれば、ノードの数をnとすると、2^nの全組み合わせとなりますが、それを「n+1」個に減らすことができます。
条件が10個あれば、2¹⁰ = 1024が10+1 = 11と、約1/93のテストとなります。
実際には、原因結果グラフを使わない場合は、全組み合わせのテストはされず、経験と勘と度胸(KKD)で適当にまびいたテストがおこなわれることが多いものです。したがって、テスト工数はそれほど変わらないものです。
しかし、論理的に大切な組み合わせを網羅したテストと、KKDで適当にまびいたテストでは、検出されるバグ数に大きな差がでます。
その結果、原因結果グラフを使用しないと、リリース後に「こんな単純なバグをテストでみつけることができなかったのか……。」となります。
≡ ソフトウェア・テストの技法
原因結果グラフを紹介していることでも有名な、マイヤーズの『ソフトウェア・テストの技法』にどう書いてあるか確認します。
※ 参考情報ですので、ここはよく分からなくても大丈夫です。ここは、読み飛ばして「おわりに」に進んでも構いません。
結論から言うと、マイヤーズの『ソフトウェア・テストの技法』で示されている原因結果グラフからデシジョンテーブルを作成する方法は、その後、同じ人が開発した市販のツールには採用されていません。
ということで、今ひとつなアルゴリズムが書いてあります。
まずは、同書のリンクから
読むたびに、「こんなことも書いてあったのか」と発見がある、素晴らしい本です。
さて、今回の内容(ANDのときのデシジョンテーブルの作成)について書いてある箇所は、77ページです。図で説明してありますので、そのまま写真として引用します。

こちらは、3つの原因a, b, cが、AND(∧)という論理関係で結ばれて結果xを出力する原因結果グラフです。CEGTestで描けば、以下の通りです。

この2つはどちらも原因結果グラフです。
したがって、マイヤーズの『ソフトウェア・テストの技法』p77, 図4.15にある説明がCEGTest版でも成り立つはずなのですがそうはなっていません。
まず、押さえておかなければならないことは、書籍の方法は「最終結果の真偽から原因方向に遡りつつデシジョンテーブルを埋めていく方法」であるということです。
説明は2つありますので、順番に確認します。
説明1:
● もしxが1ならば、a = b = c = 1となるすべての状況を列挙する。
説明1はCEGTestが生成したデシジョンテーブルで言えば、ルール1(#1)のことです。「xが1」は「xがT」ということです。
前回、「計算機上では”0以外”=True、”0”=Falseをあらわします。」と書いたとおりです。
つまり、「もしxが1ならば、a = b = c = 1となるすべての状況を列挙する。」とは、「ある一つの結果ノード(この場合はx)に着目して、その値がT(真)のロジックを確認するためには、着目している結果ノードの直下にある原因ノードa, b, cがすべてTになる全ての組合せを遡ってテストしなさい」ということです。
「すべて」が2回出てきて分かりにくいですが、ひらがなの「すべて」は、a, b, cのどれもが1という意味です。(ノードの数はa, b, cの3つに限らないので)
一方、漢字の「全て」のほうは、原因結果グラフが多段になる時にa, b, cが1となる前段階の組合せの全てという意味になります。
つまり、中間ノードがあるときに、結果ノードが真になる組合せは全部テストしなさいと言っています。(CFD法の有効系と同じです。と言いますか、こちらの方が先に発表されていますのでCFD法でも採用されているアイデアです)
続いて、説明2です。
説明2:
● もしxが0ならば、a = b = c = 0となる1つの状況だけを含む(考察3)。001, 010, 100, 011, 101, 110(a, b, cが)の状態はそれぞれ1つの状況だけをふくむ(考察2)。
こちらの日本語は難解です。「翻訳本で疑問が生じたときには、原文を当たれ」ということで、原著の該当箇所を載せます。

If x is to be 0, include only one situation where a = b = c = 0(consideration 3). For the states 001, 010, 100, 011, 101, and 110 of a, b, and c, include only one situation each(consideration 2).
同じことが書いてありますので、翻訳が間違いということではなさそうです。
実は、上記の(書籍の)方法でも組合せの数は減るのですが、無駄がありますし、かなり大変です。
また、本書籍発行後に商品化された、”SoftTest”と”Caliber RBT”と”Bender RBT”はどれも、そのような実装にはなっていません。きっと書籍発行後、ツール販売までにアルゴリズムの改良がおこなわれたものと思います。(CEGTestは、”SoftTest”や”Caliber RBT”と同じ実装です)
書籍の方法は、結果ノードxが偽のときに原因結果グラフを遡ってデシジョンテーブルを埋めていくときの話について書いてあります。
まず、直下の原因ノードであるa, b, cをチェックして、a, b, cが全て偽のケースは1つだけテストするとあります。(全部が偽のときには、全部が偽となる組合せを全てテストせずに1つだけで良いということです)
ここまでは、それで良い理由は書いていないので分からないものの手順としては明確です。問題は次の「001, 010, 100, 011, 101, 110(a, b, cが)の状態はそれぞれ1つの状況だけをふくむ」のほうです。
a, b, cの中に真と偽が混ざっているときの話が書いてあります。
つまり、a, b, cが001, 010, 100, 011, 101, 110(0は偽、1は真)の時の話です。
例えば1つ目の001は、aが偽のケースを確認しています。
001の時に真となっている原因ノードはcです。
このcについては、cが真になる組合せが複数だったとしても、1つだけテストすれば良いという方法です。
例えばcが、iとjのORで成り立っている時に、cが真になる組合せは、iとjがともに真、iが真でjが偽、iが偽でjが真の3通りありますが、1つだけでいいよということです。(実はORのルールから、iとjがともに真以外のどちらかとなります)
まとめると、他のノードの真のケースを更に遡るときには一つだけ真のケースを採用すれば良いということです。
(例題のような1段しかない場合には、全ての組み合わせとなります。逆にいうと中間ノードがあるときにだけ全組み合わせでなくなります)
と、長々と書きましたが、分かりにくいですよね。
(実務上は分からなくてもツールがやってくれますので、問題はありません)
≡ おわりに
今回は、「ロジックを網羅する」の中編でした。前回のNOTに続いてANDの場合を考えました。
書籍による方法は、あくまでも“参考情報”です。「そういう間引きかたもあるのね」と言った程度で深入りしないことをお勧めします。
次回は、今回の続きとなる「ロジックを網羅する」の後編となります。
残ったORが網羅するものについて確認していきます。といっても今回の話のOR版ですので、次回の前に考えてみることをお勧めします。(パズルを解く感じで面白いと思うかも)