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

PictMaster 5.7.1をリリースしました

PictMaster 5.7.1をリリースしました。主な変更点は以下の通りです。

【バグ修正】
・64ビット版Excel 2010でVBAのコンパイルエラーとなる問題に対処しました。

PictMaster 5.7.1は次のサイトからダウンロードすることができます。

http://sourceforge.jp/projects/pictmaster/

64ビット版Ecxel 2010使用時の不具合について

PictMaster v5.7を64ビット版Excel 2010で使用するとコンパイルエラーとなります。

これはv5.7で追加したWindows API関数の呼び出し方法が、Excel 2010の新しいVBAであるVBA7での仕様変更と互換性がないために起きる現象です。

VBA7では64ビット環境からWindows APIを呼び出す際のインタフェースが変更されました。PictMasterにはv5.7からWindows APIの関数を呼び出す処理が追加されています。VBA7ではこの呼び出し処理に関してそれまでのVBA6との互換性がなくなりました。

まもなくこの問題を修正したバージョンをリリースする予定です。Excel 2013での問題も解消されるはずです。

すでにv5.7で使用済みのPictMasterを修正したい場合は次の手順を行なって下さい。

1. 開発メニューまたはツールメニューからVisual Basicのエディタを起動する。
2. 標準モジュールフォルダ内の「テストケース生成」をクリックする。
3. VBAのコードが表示されるので先頭から15行目ほどにある次のコードを新しいコードで置き換える。

Declare Function GetAsyncKeyState Lib "User32.dll" (ByVal vKey As Long) As Long

     ↓

#If Win64 Then
    Declare PtrSafe Function GetAsyncKeyState Lib "User32.dll" (ByVal vKey As LongLong) As Long
#Else
    #If VBA6 Or VBA5 Then
        Declare Function GetAsyncKeyState Lib "User32.dll" (ByVal vKey As Long) As Long
    #Else
        Declare PtrSafe Function GetAsyncKeyState Lib "User32.dll" (ByVal vKey As Long) As Long
    #End If
#End If

4. ファイルメニューから「上書き保存」を指定する。

以上です。


※ 2014年6月16日 VBAが将来VBA8以降にバージョンアップされた場合でも問題が起きないようソースコードを修正しました。現行の最新バージョンである 5.7.3 ではすでに修正済みです。

意外と易しい !? 原因結果グラフ 結果ノードがひとつの場合

これまでの原因結果グラフは結果ノードが複数ありました。今回は結果ノードが1つだけのケースを取り上げてみましょう。CEGTestのツールでは、Fileメニューの「クラウドから読み込む」で例題をインポートすることができます。その機能を使ってNo.3の「献血を断られるケース」をインポートしましょう。そうすると以下の原因結果グラフが表示されます。

(1)


それではこの原因結果グラフをもとにデシジョンテーブルを作成してみます。この原因結果グラフには制約がありません。さらに結果ノードが1つだけです。この場合、結果に影響を与えない条件というものはありますが、プログラムが原因結果グラフの通りにコーディングされているとは限りません。そのため本来結果に影響を与えないはずの条件にもバグがないことを確認できるような特定の真偽いずれかの条件を指定する必要があります。

このデシジョンテーブルは次の手順で作成します。

(1) 原因ノードの「体調が良くない」が真かつ中間ノードの「睡眠不足条件」が偽かつ中間ノードの「安全理由」が偽である条件を決定する。
(2) 原因ノードの「体調が良くない」が偽かつ原因ノードの「睡眠不足」が真かつ中間ノードの「睡眠不足例外」が偽かつ中間ノードの「安全理由」が偽である条件を決定する。
(3) 原因ノードの「体調が良くない」が偽かつ中間ノードの「睡眠不足条件」が偽かつ中間ノードの「安全理由」が真である条件を決定する。
(4) 原因ノードの「体調が良くない」が偽かつ中間ノードの「睡眠不足条件」が偽かつ中間ノードの「安全理由」が偽である条件を決定する。

こうして作成したデシジョンテーブルを次に示します。

(2)


全部で9件のルールとなりました。手順の(1)でルール1と2、(2)でルール3と4、(3)でルール5と6、(4)でルール7と8と9が導かれます。

最低でもOR条件はどれか1つが真の場合とすべてが偽の場合、AND条件はすべてが真の場合とどれか1つが偽の場合を網羅します。そのうえでさらにリスクを低減するためにテストに必要と考えられる条件を追加していくことが基本的な手順となります。ここで作成したデシジョンテーブルは1つの例であってこれだけが正解という訳ではありません。より慎重な人はもっと多くのテストケースとなるでしょう。

参考にCEGTestが生成したデシジョンテーブルを次に示します。

(3)


全部で4つのルールとなっています。かなり少ないですが、ツールでは論理構造をもとに必要最小限のテストケースを作成するようになっているようです。それにしても4件は少なすぎる気がします。これで漏れのないテストが可能でしょうか?

例えば、プログラマが中間ノードの「睡眠不足例外」のAND条件を誤解または誤ってOR条件でコーディングした場合を考えてみましょう。生成されたデシジョンテーブルでは「睡眠不足例外」が真となるケースはルール2のみとなっています。ルール2では同時に中間ノード「安全理由」も真となっています。中間ノード「安全理由」が真なら「睡眠不足例外」は真偽どちらであっても結果ノードに影響を与えません。

プログラマが中間ノードの「睡眠不足例外」のAND条件をOR条件でコーディングしてしまったバグを見つけだすには、原因ノードの「計画を大きく下回る」と「医師の許諾あり」のすくなくともどちらか一方が偽かつ「睡眠不足」が真かつ「体調が良くない」が偽かつ「特定の病気に罹患」が偽かつ「海外から帰国4週間以内」が偽であるテストケースが必要です。ところがCEGTestが生成したデシジョンテーブルにはこれに相当するルールがありません。これではバグを見逃してしまいます。

一方、人手で作成したデシジョンテーブルでは、ルール3と4のいずれかをテストすることでバグを検出することができます。

意外と易しい !? 原因結果グラフ 入場料問題

前回に引き続き原因結果グラフを使ってデシジョンテーブルを作成してみます。今回取り上げる問題はJaSST'07 Tokyo での「三賢者、テストを語る (DTvsCEGvsCFD)」 のDTの「入場料問題」(28ページ目)です。

1


この問題から原因ノードを決めます。個人と団体、一般と小学生と6歳未満と65歳以上、県外在住と県内在住の3グループになります。それぞれのグループは、どれか一つだけが真となります。結果ノードは入場料の5種類です。県内在住の小学生の条件を決定するために中間ノードを設けます。県外在住の小学生と県内在住の小学生です。これらのノード間を接続します。原因ノードはどれか一つだけが真となるようにONE制約を指定します。こうして作成した原因結果グラフを以下に示します。

(1)


比較的単純なグラフとなりました。原因結果グラフされ描ければデシジョンテーブルへの変換は単純な作業です。例によって結果ノードが真となる条件を求めます。作成したデシジョンテーブルを以下に示します。

(2)


この表で「-」となっている部分は真でも偽でも結果ノードに影響を与えないことを意味します。

手作業ではこういうデシジョンテーブルになりましたが、CEGTestが生成したデシジョンテーブルはどうなったでしょうか。

(3)


ルール数は同じ7件ですが、手作業で「-」となった部分に真や偽の特定の条件が指定されています。手作業で作成したデシジョンテーブルと矛盾はしていませんが、どうもすっきりしません。例えば、6歳未満が真の場合、個人と県内在住には真が、それ以外は偽が指定されています。この真偽指定は結果に影響を与えないので無意味です。意味のない部分には真偽が観測できない「M」が指定されるように原因結果グラフに手を加えてみました。その生成結果と合わせて以下に示します。

(4)



(5)


MASK制約だらけになりましたが、生成されたデシジョンテーブルは手作業で作成したデシジョンテーブルとまったく同じになりました。人間なら結果に影響を与えない原因は簡単に理解できますが、ツールは理解できません。MASK制約で明示的に指定してやる必要があります。

今回の問題は過去にPictMasterに適用したことがある(PictMasterでデシジョンテーブルを作成する !?)のですが、今回はより分かりやすい方法で適用してみました。PictMasterのモデルと生成されたデシジョンテーブルを以下に示します。

(6)


(7)


今回の肝は、互いに同時には真とならない原因を同じパラメータに属する値として定義することによってパラメータ数を削減して、制約指定に強い制約が指定できるようにし、削減する必要のある不要な組み合わせが生成されないようにした点にあります。これによって「実行」ボタンのクリックだけでそのままテストケースとして使えるデシジョンテーブルが生成されるようになりました。

意外と易しい !? 原因結果グラフ

今回はデシジョンテーブルを作成するテスト技法のひとつである原因結果グラフを取り上げます。原因結果グラフと聞くと難しそうと感じる人が多いと思いますが、じつは私もそうでした。原因結果グラフを描くことは少し調べれば描けるようになります。いきなり複雑なものは無理ですが、順を追って手をつけていけば複雑なものも描けるようになります。問題は描いた原因結果グラフからからデシジョンテーブルを作成するやり方が分からないということでした。

現在は原因結果グラフを描いてデシジョンテーブルを生成してくれるCEGTestという便利なツールがありますが、このツールだけを使っていては、原因結果グラフからからデシジョンテーブルを作成するやり方はブラックボックス化のままです。途中経過を表現するカバレッジ表が生成されるので、その意味が理解できればデシジョンテーブルをへの変換方法も理解できるはずですがこれがなかなか面倒そうです。

原因結果グラフさえ描ければデシジョンテーブルへの変換は簡単にできる、と某サイトにあったのでそれならばと挑戦してみることにしました。

原因結果グラフは原因ノード→結果ノードで構成されています。間に中間ノードが入ることもありますが、これは原因結果グラフを分かりやすくするために設けるものです。この原因結果グラフからデシジョンテーブルを作成しようとする場合、原因ノードから考えると分かりにくくなります。その逆に結果ノードから考えて、その結果が真となるには原因ノードが真偽どちらでなければならないかと考えると分かりやすくなります。

ボウリングの得点を計算する原因結果グラフを例にあげてやり方を説明します。この例は以下のサイトに掲載されているものを参考にしています。

http://www.cs.is.saga-u.ac.jp/syllabus/SE/testing.pdf

ボウリングの得点計算ルールは次の通りです。

(1) ストライクの場合は,次の2投の合計+10を加算する
(2) スペアの場合は,次の1投+10を加算する
(3) それ以外の場合は,1フレーム中で倒した本数を単純に加える
(4) 10フレーム目は変則で,2投あるいは3投した合計をそのまま加算する

今回は(4)のルールは難しいので省略し、(1)から(3)のルールをカバーすることにします。

原因結果グラフを作成するにあたってまず原因ノードを洗い出します。

ボウリングの得点計算に必要なのは次の情報です。これが原因ノードとなります。

1.フレームの1投目の得点
2.直前のフレームがスペア
3.直前のフレームがストライク
4.2フレーム前がストライク

ここで「直前のフレームがスペア」と「直前のフレームがストライク」は、どちらかが真または両方とも偽となる排他的論理和(exclusive or)の関係にあります。

この情報から得点を計算することになります。得点の計算パターンとなる結果ノードは次の通りです。

A.現在のフレームに加点する
B.現在と直前のフレームに加点する
C.現在、直前、2つ前のフレームに加点する

原因ノードから結果ノードをつなごうとすると難しいので分かりやすくするために中間ノードを設けます。中間ノードは結果ノードのどれかと接続されます。ここで中間ノードがどれかの結果ノードに接続できるように原因ノードと中間ノードを論理的に接続します。

中間ノードは得点の計算パターンに応じて次の6個となります。

a.1投目 直前のフレームがオープン
b.2投目 直前のフレームがストライクでない
c.1投目 直前のフレームがスペア
d.1投目 直前のフレームがストライクで2フレーム前がストライクでない
e.2投目 直前のフレームがストライク
f.1投目 直前及び2つ前のフレームがストライク

2つ以上の原因ノードの論理的な組み合わせによって中間ノードのどれかと接続します。

ここまでできたら次に中間ノードと結果ノードを接続します。中間ノードを設けたことで結果ノードとの接続が簡単になります。

以上で原因結果グラフが完成しました。グラフの作成に便利なCEGTestというツールで作成した原因結果グラフを以下に示します。

(3)


CEGTestは原因結果グラフを作成すると自動的にデシジョンテーブルも生成してくれますが、ここでは機械的に簡単にできると言われている、原因結果グラフからデシジョンテーブルへの変換を手作業で行なってみましょう。

結果ノードから原因ノードの条件を決めていくのがコツです。まず最初に結果ノードの「現在のフレームに加点」が真となる条件を調べます。すると中間ノードの「a」と「b」のOR条件で真となることが分かります。続いて「a」が真となる原因ノードの条件を調べます。「そのフレームの1投目」が真かつ「直前のフレームがスペア」が偽かつ「直前のフレームがストライク」が偽の場合が1つです。この場合は「2フレーム目がストライク」は真偽どちらでも結果ノードに影響を与えないことが分かります。もう1つは「そのフレームの1投目」が偽かつ「直前のフレームがストライク」が偽の場合です。この場合は「直前のフレームがスペア」と「2フレーム前がストライク」は真偽どちらでも結果ノードに影響を与えないことが分かります。

続いて同様に次の結果ノードが真となる条件を原因結果グラフから調べていきます。

このようにして作成したデシジョンテーブルを次に示します。

(4)


「-」となっている部分は結果に影響を与えないことを意味しています。実際にやってみると、原因結果グラフからデシジョンテーブルへの変換が驚くほど簡単にできることが分かると思います。原因結果グラフが難しいと言われるその大部分は原因結果グラフ作成の難しさにあります。


CEGTestは原因結果グラフを作成するとデシジョンテーブルを生成してくれる機能があります。今回生成されたデシジョンテーブルを次に示します。

(6)


このデシジョンテーブルの原因ノードには、結果に影響を与えない「M」という表記がありません。替わりに「F」という偽の条件が指定されています。「M」にするにはMASK制約などを指定する必要がありそうですが、その方法が分かりません。さらにルール数が6件ではなく8件となっています。2件増えている個所は真偽どちらでも結果に影響がないケースで真と偽がそれぞれ明示的に指定されている個所です。

CEGTestの使い方は分からなくともとりあえずは原因結果グラフさえ描ければ、CEGTestを使わなくても原因結果グラフから手作業でデシジョンテーブルを作成することは、それほど難しくなくできそうなことは分かりました。そういう意味で原因結果グラフを簡単に描くことができるという点だけでもCEGTestを使う価値は十分にあります。


さて手作業で作成したデシジョンテーブルですが、このままテストで使っていいかというとそうはいかないようです。論理が複雑なうえに結果に影響を与えない条件が多くあります。原因結果グラフからは圧縮したデシジョンテーブルしか得ることができません。デシジョンテーブルを圧縮する危険性については「デシジョンテーブルの圧縮(整理・縮小)は要注意」で指摘したとおりです。今回は欠陥の見逃しリスクを減らすために圧縮していないデシジョンテーブルを作成することにしましょう。今回のケースではCEGTestが生成したデシジョンテーブルには「-」に相当する「M」が含まれていないので、圧縮前のデシジョンテーブルを得ることができません。


デシジョンテーブルで「-」となっている部分に真偽の両方を割り当てて圧縮前のデシジョンテーブルを再現したものを次に示します。

(5)


全部で12件となりました。機械的に再現を行なうともっと多くなりますが、「直前のフレームがスペア」と「直前のフレームがストライク」は同時には真とならないのでそのテストケースを除外してあります。


これでテストに使えるデシジョンテーブルを得ることができました。とっつきにくい印象のある原因結果グラフですが、案外簡単にデシジョンテーブルに変換できることがお分かりいただけたと思います。

ついでにこの問題にPictMasterを適用してみましょう。

使用するパラメータは投球回数、直前のフレーム、2つ前のフレームです。投球回数には1投目と2投目、直前のフレームと2つ前のフレームにはミス、スペア、ストライクの値を使用します。組み合わせるパラメータ数に3を指定し、生成される組み合わせは2×3×3=18件になります。このそれぞれの組み合わせに結果表を使って得点計算を指定することにします。

PictMasterのモデルは次の通りとなります。

(3)


組み合わせが生成されたら「整形」ボタンを使って「結果内容」をキーとして並べ替えを行ないます。こうすると結果に影響を与えないパラメータが分かるのでその組み合わせは1通りを残して後は削除します。こうして得られた最終結果は以下の通りです。

(4)


原因結果グラフから手作業で作成したデシジョンテーブルより2件多くなっていますが、これはフレームの投球結果がミスを加えた3件になっているためです。PictMasterを使ってデシジョンテーブルを作成するには制約表を使用する必要のある場合がありますが、今回のケースでは不要でした。