組み合わせテストケース生成ツール 「PictMaster」 とソフトウェアテストの話題 -23ページ目

単適合デシジョンテーブルと多重適合デシジョンテーブル

デシジョンテーブルは決定表の名称で1984年にJISのX0125として規格化されました。そこで規格化されたデシジョンテーブルは「単適合デシジョンテーブル」だけであり、「多重適合デシジョンテーブル」は対象外となっています。

単適合デシジョンテーブルとは強い制約をかけるものであり、どれか1つのルールと一致したアクションだけが実行されます。これに対して多重適合デシジョンテーブルとは弱い制約をかけるものであり、すべてのルールが評価され、2つ以上の一致したアクションが実行されます。

プログラムでいえば、単適合デシジョンテーブルの処理は if then の分岐が段階的に枝分かれしていくプログラムです。判定文を2回実行すると2×2=4つのルートに分かれる並列型のプログラムです。

一方、多重適合デシジョンテーブルの処理は if then の分岐がもとに戻り、枝分かれしていかないプログラムです。判定文を2回実行しても同じルートに戻る直列型のプログラムです。

プログラムには並列型のプログラムもありますが、直列型のプログラムもそれに劣らず多く見かけることができます。直列型のプログラムの方が多いかもしれません。

デシジョンテーブルは複雑な論理を表現する技法ですが、現在用いられている単適合デシジョンテーブルで直列型の論理を表現しようとすると、結構面倒なことになります。

例えば次の料金割引適用の仕様があったとします。

子ども割引
  年齢が12歳以下
高齢割引
  年齢が60歳以上
特別割引
  女性で今日の曜日が水曜日
クーポン割引
  クーポン券持参

それぞれの割引は重複して適用できる。


これを現在用いられている単適合デシジョンテーブルで表現しようとすると、年齢が3通り、性別が2通り、曜日が2通り、クーポンが2通りで合計24通りの条件の組み合わせ(ルール)を定義する必要があります。しかし、多重適合デシジョンテーブルを用いれば次の4通りで済みます。

1


デシジョンテーブルが複雑な論理を簡潔に表現する技法であるならば、多重適合デシジョンテーブルも許容されてしかるべきという考え方も成り立ちます。

この例の多重適合デシジョンテーブルをPictMasterでモデル化したものを次に示します。

2

3


単適合デシジョンテーブルとの違いは、アクションを多重に有効にできるようにするためにアクションではなく条件を制約条件としている点です。このモデルでルールが9個に増えているのは、PictMasterでは割引のない場合も定義する必要があるためです。このモデルの生成結果を次に示します。

4


24件のテストケースとなりました。この24件をすべてテストする必要があるかというとそうではありません。これまでの記事で説明したように、この処理ではプログラムが直列型になるので、すべての割引が適用になる場合、どれか1つだけが割引となる場合、そしてどの割引も適用にならない場合のテストを行なえば十分といえます。

多重適合デシジョンテーブルはテスト実施の場面では利用価値はなさそうですが、ソフト開発やテスト設計では仕様を簡潔に分かりやすく表現できる点で役に立つと考えられます。


※ この記事は、日本信頼性学会誌 : 信頼性 34(6), 397-404, 2012-08-01の「ソフトウェア論理の設計/検証における決定表の応用」を参考にさせていただきました。

アクションが重複するデシジョンテーブル

複雑なデシジョンテーブルを作成すると、気が付かないうちにアクションが重複したデシジョンテーブルを作成してしまうことがあります。アクションが重複したデシジョンテーブルとは、ある条件の値の組み合わせの1つが異なる2つ以上のアクションに重複して定義されることを指します。アクションが重複したデシジョンテーブルのもっとも単純な例を次に示します。

1


この例では、制約1と制約2で条件1と条件2の値がともにYesの場合、アクション1とアクション2の両方が起動されることになります。この例ではすぐに重複していることがわかりますが、複雑なデシジョンテーブルではわかりにくい場合があります。意図的なアクションの重複を認める考え方もありますが、間違えて重複してしまった場合との区別がつかないため、こうした場合はもう1つアクションの行を追加し、そこで定義すべきでしょう。

PictMasterでは、こうした意図しないアクションの重複を結果表を使用して自動的に検出することができます。この例で重複を検出する結果表を次に示します。

2


結果表の結果内容の欄には各アクションを記入します。条件の欄にはそのアクションとなる条件の値の組み合わせを記入します。このとき、制約表が空欄の場合はその条件の全ての値を記入します。任意の値を意味する「-」の場合も同様にその条件の全ての値を記入します。こうしてから生成を行うと次のエラーメッセージが表示され、アクションが重複していることがわかります。

3




アクションが重複するデシジョンテーブルの他の例を次に示します。

4


これは入力された月と日の値をチェックする処理で、エラーが見つかればそのエラーに対応したエラー番号を出力する処理です。この処理ではエラーをすべてチェックし、2つ以上のエラー番号を出力することが可能となっています。このままでは1つのルールで複数のエラー番号を定義できていません。ではどうするか。この場合はアクションの行を増やすことにします。

1

2


Err.で始まるパラメータがアクションです。Err. の「日がその月の最大日を超える」は、月が1~12の範囲内にあって、かつ日が1以上の場合のみ有効となります。それ以外の場合は常にNoとなります。制約7ではエラー番号を出力しない組み合わせを指定しています。

ここではサブモデルは使用せず、環境設定の組み合わせるパラメータ数に4を指定しています。条件の数が4つなので組み合わせるパラメータ数はそれと同じ4つあれば十分です。生成されたテストケースを次に示します。

3


全部で7件のテストケースとなりました。同時に出力されるエラー番号は2つまでであることが分かります。

※ 文中の図表に誤りがありましたので4月8日に修正しました。

優先順を持つ条件、独立して有効となる複数のアクションがあるデシジョンテーブルを安全に圧縮する

デシジョンテーブルの中には優先順を持つ条件を含むものがあります。また独立して有効となる複数のアクションを持つデシジョンテーブルもあります。今回はこうしたデシジョンテーブルを安全に圧縮する方法を紹介します。

説明に用いる例題の仕様は、JaSST 2013 東海で有限会社デバッグ工学研究所の堀田さんが発表された「論理の設計とテストを考える <CFD++、その目指すところ>」から引用しています。
これはある会場の入場券の割引率を決定するものです。この仕様を以下に示します。

仕様 入場券の割引率

(1) 60歳以上なら老齢割引(20%)
(2) 12歳以下なら子供割引(40%)
(3) 女性で水曜日に限り特別割引(15%)
(4) 割引券持参ならクーポン割引(15%)

をそれぞれ行う。これらの割引は加算されない。 

さらに、上記に以下が加算される。 

(5) 住民であれば住民割引(10%)
(6) ネット購入の場合はネット割引(10%)
(7) 18時以降の入場は夜間割引(10%)

この3つの割引は同時加算が可能。 

「これらの割引は加算されない」というのは、各割引は排他の関係にあり、同時に割引が可能なのは高々1つだけということです。「この3つの割引は同時加算が可能」というのは、各割引は独立して適用され、同時に2つ以上の割引が適用される場合があるということです。

排他の関係にある割引は、適用される条件を満たす割引が2つ以上ある場合、割引率の高い1つの割引だけが適用されます。仕様では特別割引とクーポン割引の割引率が同じですが、この2つの割引が適用される条件を同時に満たした場合は、特別割引が適用されるものとします。

この仕様から条件とその値を求めてみます。

年齢
  65歳以上、12歳以下、その他
性別
  男性、女性
曜日
  水曜日、水曜日以外
割引クーポン
  あり、なし
地域内住民
  Yes、No
ネット購入
  Yes、No
18時以降の入場
  Yes、No

この条件をそのまま組み合わせると、3×2×2×2×2×2×2=192件のテストケースとなります。このままでは仕様の割にテストケース数が多すぎるので何とか削減したいところです。

ここで仕様に注目してみます。仕様の(1)~(4)は、どれか1つが条件を満たせば割引率が決定できます。この場合、割引率の高い条件から判定を行なうことになります。条件を満たすことが分かった後は残りの条件は判定する必要がありません。この処理をコーディングすると次のようになります。

if 年齢 = 65歳以上 then
割引率 = 20%
return
end if
if 年齢 = 12歳以下 then
割引率 = 40%
return
end if
if 性別 = 女性 and 曜日 = 水曜日 then
割引率 = 15%
return
end if
if クーポン券 = あり then
割引率 = 15%
return
end if

各条件が満たされた場合はそれ以降の処理を行いません。おそらくほとんどのプログラマは仕様の(1)~(4)の処理をこのようなモジュールにまとめると思われます。そうするとこの仕様部分に関しては各条件の組み合わせは必要なく、それぞれの条件が1つだけ有効の場合とすべて無効の場合のテストケースがあればよいことになります。

仕様(5)~(7)は、各条件は独立して有効となります。この仕様の部分のコーディングは次のようになります。

if 地域内住民 = Yes then
割引率 += 10%
end if
if ネット購入 = Yes then
割引率 += 10%
end if
if 18時以降の入場 = Yes then
割引率 += 10%
end if

ここでは単純化のため、割引率の加算にしています。各条件が満たされた場合でもそれ以降の処理を行います。おそらくほとんどのプログラマは仕様の(5)~(7)の処理をこのようなモジュールにまとめると思われます。そうするとこの仕様部分に関しては各条件の組み合わせは必要なく、3つの条件がすべて有効の場合とすべて無効の場合、それに各条件がそれぞれ1つだけ有効の場合のテストケースがあれば十分ということになります。

以上の考察をもとにして、このデシジョンテーブルを生成するPictMasterのモデルを次に示します。

1


サブモデルで組み合わせるパラメータに年齢、性別、曜日、クーポン割引券、地域内住民、ネット購入、入場時間の7つを指定しています。サブモデルでこれらのパラメータの全数組み合わせを指定しています。環境設定の組み合わせるパラメータには1を指定します。全数組み合わせですから生成される組み合わせは1種類しかありません。そのためテストケースの生成は1回だけで済みます。

このモデルではパラメータ老齢割引(20%)を制約条件としています。制約条件はアクションならどのアクションでもいいのですがこのモデルではたまたま老齢割引(20%)を選んでいます。このアクションがありとなしになるすべての組み合わせを制約表で指定します。今回は仕様を考察した結果、すべての条件とアクションの組み合わせを網羅する必要はなく、各アクションの特定の組み合わせごとに必要な組み合わせを網羅することになります。

ここで赤色で示した制約表の値は、条件が有効となる(割引が適用される)値であることを示しています。制約1ではすべての条件が有効になっていますが、これは割引を適用する条件判定処理が正しい優先順でコーディングされているかを確認するためです。同様に制約2と制約3も重複して複数の条件を有効にしています。

各制約条件で同じ値の制約条件は必ず隣り合う列となるようにします。こうすることでそれらの制約条件がそれぞれのOR条件で定義されるようになります。

この制約表には空きのセルがありません。ということは、この制約表がテストケースそのものだということです。生成ボタンをクリックすることでテストに使えるテストケースが生成されます。

正確に言うと制約表に空きのセルが1つもない場合は、組み合わせを新たに「生成」する必要はなく、制約表の組み合わせをそのままテストケースの形で出力するだけでよいのでサブモデルを使用する必要はありません。環境設定で組み合わせるパラメータ数に1を指定するだけで済みます。

2


全部で5件となりました。この結果は「論理の設計とテストを考える <CFD++、その目指すところ>」に掲載されているデシジョンテーブルと等価です。今回は仕様からほとんどのプログラマがこうコーディングするだろうとみてテーブルの圧縮を行なっています。徹底したテストが必要な場合は、圧縮ではなく組み合わせが必要な部分を制約表で空欄にすれば、後はPictMasterがその部分の組み合わせだけを展開したデシジョンテーブルを生成してくれます。仕様変更があった場合も手作業でデシジョンテーブルを作成するより簡単にわかりやすく修正することができます。

デシジョンテーブルを安全に圧縮する その2

前回に引き続いてデシジョンテーブルを安全に圧縮する方法を説明します。今回も説明に使用する例題は「強い制約とサブモデルを用いてデシジョンテーブルを自動生成する」で取り上げた水族館の入場料発券システムを例に説明します。

このシステムの仕様の例外規定を次に示します。

(1) 小学生以下、都内在住・在学の中学生は無料です。中学生は生徒手帳を持参してください。
(2) 障害者手帳・愛の手帳をお持ちの方と、その付添者原則1名は無料です。
(3) 65歳以上の方は、年令の証明となるものをお持ちください。

今回はこの規定の(1)に注目します。中学生の場合は都内在住・在学であれば他の条件に関わらず無条件に無料となります。これまではこの条件のパラメータと値は次のように定義していました。

中学生都内在住 Yes, No
中学生都内在学 Yes, No

これは2×2=4通りの組み合わせになります。この4通りのうち、入場料が無条件に無料となるのは中学生都内在住がYesの場合と中学生都内在学がYesの場合の2通りです。中学生都内在住がYesかつ都内在学Yesのケースは前の2通りと同じ無料になるのでこれを判定する必要はありません。そうすると中学生の場合のパラメータと値は次のように置き換えることができます。

中学生 都内在住, 都内在学, どちらでもない

これまでの4通りから3通りに削減することができました。今回のように同時に2つがYesになる場合を判定する必要がない場合は、2値ではなく3つ以上の値を持つ1つの多値のパラメータに集約することでテストケースを削減することができます。

またにYes/Noの2値をとるタイプのパラメータで同時に2つのパラメータがYesとなることがない場合は次のように値に具体的な名称を使用することでパラメータを減らし、テストケース数を削減することができます。

男性  Yes, No
女性  Yes, No
成年  Yes, No
未成年 Yes, No

は2×2×2×2=16通りですが、

性別 男性/女性
年齢 未成年/成年

は2×2=4通りで済みます。ただし、これはデシジョンテーブルを手作業で作成する場合の話しです。PictMasterでデシジョンテーブルを作成する場合は、制約表を用いて組み合わせできない組み合わせは除外されるため、上の場合の例ではYes/Noのパラメータを使用しても結果的には4通りの組み合わせしか生成されません。それでも最初からパラメータと値を適切に定義する方が制約を指定しなくて済むため望ましいといえます。

話を元に戻して中学生の場合のパラメータを1つに集約したPictMasterのモデルを次に示します。

1

2


パラメータ中学生に値「-」が追加され、制約1と2が変更されています。これは中学生以外の場合は値「-」とのみ組み合わせるようにするものです。このモデルの生成結果を次に示します。

2_


これまでの28件が24件に減少しました。このテストケース数はさらに削減することができます。

テストケース数は24件となりましたが、実際のプログラムでは料金を決定するルートが24通りもあるとは考えにくいでしょう。その意味でこのテストケースはまだ「無駄の多い」テストケースだといえます。そこで中学生の場合は都内在住・在学であれば他の条件に関わらず無条件に無料となるという仕様を考えてみましょう。これはプログラムの料金を決定するルートに都内在住と在学の両方のケースがあればテストとして十分であるということができます。

そこでテストケース数を削減するために都内在住と在学をエイリアスとし、1つの値にしてみます。この生成結果を次に示します。

5


テストケース数が24件から20件に減少しました。中学生のテストケースを見てみると、都内在住と都内在学が均等に表れています。このテストケースではテスト漏れの可能性を完全になくすことはできませんが、テスト漏れのリスクはかなり小さいと考えられます。少々のリスクを許容できるならこのテストケースでのテストを考慮してもよいでしょう。

デシジョンテーブルを安全に圧縮する

これまでの記事でデシジョンテーブルを圧縮するとテスト漏れが起きることを説明してきました。では必ず圧縮していないデシジョンテーブルを使用しなければならないかと言うとそうではありません。条件によってはかなり安全に圧縮できる場合があります。前回の「強い制約とサブモデルを用いてデシジョンテーブルを自動生成する」で取り上げた水族館の入場料発券システムを例に説明します。

このシステムの仕様の例外規定を次に示します。

(1) 小学生以下、都内在住・在学の中学生は無料です。中学生は生徒手帳を持参してください。
(2) 障害者手帳・愛の手帳をお持ちの方と、その付添者原則1名は無料です。
(3) 65歳以上の方は、年令の証明となるものをお持ちください。

この規定の(2)に注目します。これは発券システムの区分3のラジオボタンで、一般、障害者手帳提示、愛の手帳提示、障害者の付添者のどれか1つを指定することになっています。一般以外が指定された場合はいずれの場合も無料になります。ここの部分のプログラミングで最も合理的でない例を図1に示します。

1


このプログラムでは結果のルートが4つあります。こうしたプログラムをする人はいないとは言えませんが非常に少ないと考えられます。一方、ほとんどの人は図2のプログラミングを行なうでしょう。

2


圧縮していないデシジョンテーブルは、図1のプログラムでもテスト漏れは起きませんが、こうしたプログラミングを行なう人が非常に少ないと考えられるなら、図2のプログラムを前提として圧縮を行なったデシジョンテーブルを使用するという選択肢があります。この場合、テスト漏れのリスクはゼロにはなりませんが、テストのコストに比べてリスクが無視できるほど小さいならば圧縮したデシジョンテーブルを使用してもよいことになります。

この場合のデシジョンテーブルの圧縮は、PictMasterに与えるパラメータ区分3の値を、障害者手帳提示, 愛の手帳提示, 障害者の付添者, 一般の4つから、一般を除いた残り3つをエイリアスに指定し、実質的に2つの値に変更することによって行なうことができます。変更したPictMasterのモデルを次に示します。

1


こうして生成したテストケースを表1に示します。

2


テストケース数が28件となりました。これは圧縮していない場合の56件の半分です。結果のルートが4つある部分を2つに圧縮したことにより、半減しました。前回の生成結果と条件の並びが異なっていますが、どのような並びであってもこの圧縮は有効です。このことは重要です。特定のプログラミングを前提とした圧縮は、ここで示した条件をクリアしない限りすべきではありません。

このように条件によっては圧縮してもテスト漏れの可能性を無視できるほど小さくできる場合があります。こうした条件はここで説明した以外にもいくつかあります。