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

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

モデルベースドテストについて学んでみよう - その4

The model

 第3回では、MBTツール「MISTA」を使って、ブロックゲームのファンクションネットを完成させました。

www.kzsuzuki.com

 第4回では、できあがったモデルでシミュレーションを行ったうえで、テストケースの自動生成をやってみましょう。やっとモデルベースドテストらしくなってきますね。

モデルをシミュレーションしてみる

 では、アノテーションで指定した初期状態と少し変更するとともに、終了状態も追加してみましょう。

INIT ontable(B1), ontable(B2), on(B3, B2), clear(B1), clear(B3)
GOAL clear(B1), on(B1, B2), ontable(B2)

 ブロックを3つにしています。
 初期状態でテーブルの上に直接乗っているのはB1とB2。B2の上にB3が乗っています。
 終了状態は、テーブルの上に直接乗っているのがB2で、そのうえにB1。B1の上には何もありません。B3については不問(任意のプレースを許容)です。

 第2回で行ったシミュレーションを行ってみると、想定した通りに動く(許される遷移が可能で、許されない遷移が不可能である)ことがわかります。

f:id:kz_suzuki:20200429102021p:plain
モデルのシミュレーションでランダムプレイを行う

 Start Random Simulation というモードでは、遷移先の選択肢をMISTAが選んで、次々に先に進めてくれます。
 上の図だと、ブロックが上からB1・B2・B3と重なっている状態になっています。

 また、Verify Goal State Reachability(終了状態到達可能性の検証)という機能を使えば、指定した初期状態から終了状態に到達することができるかをチェックすることができます。
 今回のモデルと初期/終了条件で実行すると、以下のような結果とシーケンスが導出されます。

Goal state: clear(B1), on(B1, B2), ontable(B2) is reachable.
Initial state: clear(B1), clear(B3), on(B3, B2), ontable(B1), ontable(B2)
Resultant state: clear(B1), clear(B3), on(B1, B2), ontable(B2), ontable(B3)
Firing sequence:
1. unstack(B3, B2)
2. putdown(B3)
3. pickup(B1)
4. stack(B1, B2)

 ブロックB2に乗ったB3を持ち上げ、テーブルの上に下ろす。テーブルの上のB1を持ち上げ、B2の上におく。という手順であることがわかります。*1

 このように、シミュレーションや経路探索の機能を用いることで、モデルの妥当性を検証することができます。

テストを導出する

 モデルが確認できたので、いよいよテストケースの生成をやってみましょう。

 MISTAでは、いくつかのカバレッジ基準に基づいて、「テストツリー」を自動生成することができます。
 たとえば、Transition Coverage(トランジション網羅)というカバレッジ基準では、すべてのトランジション*2が最低一度は現れるようにテストケースが生成されます。上のモデルだと、以下の通り。

f:id:kz_suzuki:20200429101355p:plain

 トランジションがツリーで表現されています。テストケースとしては以下の3つになります。

  • B1を持ち上げる→B1をテーブルの上に下ろす (pickup(x)putdown(x)をカバー)
  • B1を持ち上げる→B3の上に下ろす(pickup(x)stack(x, y)をカバー)
  • B3をB2の上から持ち上げる(unstack(x, y)をカバー)

 これですべてのトランジションが網羅できています。

 State Coverage(状態網羅)は、状態遷移図における状態網羅とは少しイメージが違います。

 状態遷移図で状態網羅というと、状態ノードを一度は通ることを指しています。
 一方第2回で述べたように、ファンクションネットでいう「状態」とは、トークンがプレースに配置されるパターンのことを指しています。よって「状態網羅」も、プレースの網羅ではなく、トークン配置のパターンの網羅を意味しています。

 (トークンではなく)ブロックの配置を考えてみると、3つのブロックについて以下のパターンがあります。

  1. すべてのブロックがテーブルの上にある。・・・1パターン
  2. 2つのブロックがテーブルの上にあり、残り1つはアームにつかまれている。・・・3パターン
  3. 2つのブロックが重なっており、残り1つはテーブルの上にある。・・・6パターン
  4. 2つのブロックが重なっており、残り1つはアームにつかまれている。・・・6パターン
  5. 3つのブロックが重なっている。・・・3パターン

 たとえば5.について上からブロックA・B・Cという配置であれば、

clear(A), on(A, B), on(B, C), ontable(C)

という状態になります。A、B、Cの順列を考えると、6パターンあることがわかりますね。
 このように、プレースは4つ、ブロックは3つですが、状態、つまりトークン配置は19パターンもあるというわけです。

 状態網羅を指定してテストケースを導出すると、以下のようになります。

f:id:kz_suzuki:20200429101631p:plain

 これを丁寧に追っていけば、上の19パターンがテストケース群のシーケンスのどこかでカバーされていることがかります。

テストケースから自動テストスクリプトへ・・・

 さて、モデルからテストケースが導出できるのはわかったのですが、モデルは実際のプログラム自体ではありません。
 MISTAでは、model-implementation mapping (MIM) という情報を通して、モデルとプログラム実装と関連付けることで、テストケースだけでなくテストコードまで生成し、さらに実行することができます。が、そこまでやる体力はない・・・。

 どなたかMISTAをお持ちの方は、ぜひ試してみてください!
 その5はコチラ。

www.kzsuzuki.com

*1:これは唯一のシーケンスではありませんが、なぜこのシーケンスが選択されたかはわかりません。。

*2:「イベント」と言った方がわかりやすいかもしれない。すべてのイベントを通ることで、すべてのトランジションも網羅される。