モデルベースドテスト(Model-based Testing、MBT)について学ぶシリーズの第2回です。2018年アドベントカレンダーの2日目でもあります。
第1回を振り返ってMBTとさくっと説明すると、「要求・仕様に基づいてモデルを描き、そこからテストケースを生成する」というものでした。ISTQBのシラバスを読みながらMBTを理解していくことを考えていましたが、やっぱりツールを使ってみます。
今回使うのは、「MISTA」というツール。
クイックガイドに掲載されているモデルを作成してみましょう。
MISTAとは
Model-based Integration and System Testing Automation の略で、モデルベースのテスト生成ツールです。
アーカイブはBoise州立大学で配布していたのですが、そのサイトが消えている・・・。もしかするともう公開されていないのかもしれません。
MISTAの特徴は以下の通りです。
- 描いたモデルに対し、シミュレーションを行うことができる。
- モデルから、複数のカバレッジ基準に合わせて、テストケースを自動生成できる。
- モデルなどの情報から、目的の言語に合わせて実行可能なテストコードを生成し、テスト対象システムで実行することができる。
- モデルとして、function net、UML protocol state machine、contractsという3つがサポートされている。
モデルなど、と書きましたが、具体的には以下の3つです。
- MISTAで描いたモデルそのもの
- モデルと実装を関連付けるMIM(model-inplementation mapping)
- プログラミング言語ごとに必要な補助コード
モデル作成とテストケース生成は1.だけでもできますので、この記事ではそれをやってみましょう。
MISTAでファンクションネットを描いてみる
上述の通り、MISTAは3つのモデルをサポートしています。5つのものから選ぶのはいい! 3つのものから選ぶのもいい! だが「4つ」のものから選ぶとよくない事が起こるんだ。
今回は、function net(以降、「ファンクションネット」を使ってみましょう。
なぜなら、MISTAのデフォルトがファンクションネットですし、もっというとcontractsって何かわからないからです。契約プログラミングの文脈なのでしょうか・・・?
そうはいっても、ファンクションネットが何かを知らない方もいるでしょう(わたしです)。
ですので、MISTAでモデルを実装しながら、雰囲気をつかんでいくことにしましょう。わたしも同じ流れで理解しているだけなので、嘘・不正確があったらごめんなさい。
実装する仕様
ガイドでは、ブロックゲームを例にしています。
ブロックゲームの概要はこんな感じ。
- テーブルの上に、複数のブロックが乗っている。
- テーブルの上に直接乗っているものもあれば、他のブロックの上に乗っているものもある。
- アームを使ってブロックをつかむことができる。
- 一度につかめるブロックは一つだけ。
- 他のブロックの下にあるブロックはつかめない。
- アームでつかんだブロックは、テーブルの上か、他のブロックに乗せることができる。
- 初期の配置(INIT)から、指定された配置(GOAL)にもっていくことが目的。
この仕様から導出される操作は、以下の4つです。
pickup(x)
: ブロックxをテーブルから上げるputdown(x)
: ブロックxをテーブルに下ろすstack(x, y)
: ブロックxをブロックyに上に下ろすunstack(x, y)
: ブロックxをブロックyの上から上げる
また、ブロックの状態は、以下の4つです。
ontable
: テーブルの上にあるholding
: アームがブロックをつかんでいるon
: 他のブロックの上にあるclear
: 他のブロックの下にない
ものすごくめんどくさかったのですが、がんばって絵を描いてみました。
状態遷移図に慣れた人は違和感をもったかもしれません。ブロックの状態同士がMECEではないのでは?と。たとえば、on
とclear
は両立しますよね。
実はファンクションネットにおいては、これらを「状態」と呼ぶのは適切ではないのですが、説明は後ほどとして、暫定的に「状態」と呼んでおきます。
とりあえず描き始めてみる
まず、ontable
とholding
という2つのブロック状態のノードを置いてみましょう。
ファンクションネットではこのノードのことを、Placeと呼んでいます。この記事では「プレース」と呼ぶことにしましょう。
状態遷移図だと、状態と状態の間の矢印が遷移を表しています。
一方、ファンクションネットにおいては、あるプレースから別のプレースへの遷移の間にイベントのノードを置き、プレースからイベントへ、イベントからプレースへ矢印が引かれます。MISTAではこのノードのことを Transision と呼んでいるのでちょっとややこしいのですが、この記事では以下のように呼びます。
- トランジション: プレースからプレースへの移動
- イベント: トランジションを起こす契機となる事象
テーブルの上にあるブロックをアームで持ち上げて、その後にまたテーブルに下ろすモデルを描いてみましょう。この2つの四角が、イベントです。
とりあえず、遷移の輪が閉じました。
変数と初期状態を追加する
ここでモデル検証のため遷移のシミュレーションをしたいのですが、追加しなくてはならない情報がまだいくつかあります。
pickup(x)
では、テーブルの上にある1個以上のブロックの中から、特定のブロックx
を選んで持ち上げることになります。
ですので、プレースontable
からの矢印に「x」というラベルを付けます。プレースholding
に遷移するのも同じブロックですから、そちらの矢印にも「x」を付けます。変数x
を受け渡すことで、「持ち上げたブロックと、アームがもっているブロックが同一である」ことを意味しているわけです。
同じように、putdown(x)
に入る矢印、出る矢印にも「x」というラベルを付けます。
なお、変数のスコープは分かれており、上の遷移のx
と下のx
は別モノとして扱われます。
次に初期状態を指定します。
モデルの中に Annotation(アノテーション)というものを置き、その中に記述することになります。
ここでは、2つのブロック「B1」「B2」が、ともにテーブルの上にある(積まれていない)としましょう。
以下のような記述になります。
INIT ontable(B1), ontable(B2)
これでシミュレーションの準備ができました。
モデルのシミュレーション
シミュレーションを実行してみましょう。実行画面はこんな感じ。
Simulation Contorol Panel というウィンドウが表示され、モデルも色づいています。
モデルの方から見てみましょう。
プレースの中に、何やら青い点が出現していますね。これを「トークン」と呼びます。ブロックを2つ定義していますので、トークンも(最低)2つあります。初期状態はともにontable
としましたが、それを表現しているのがこのトークンなのです。
個々のブロックに対応するトークンがその瞬間、どのプレースに配置されているか。実はこれこそが、システムの「状態」と呼ぶべきものです。各ブロックの状態もトークンの配置の組み合わせで表現されるため、プレースは排他である必要がありません。
赤く塗られたイベントは、この状態で実行可能なイベントです。2つのブロックがともにテーブルの上にある状態なので、アームはどちらかを持ち上げることができます。
次に、パネルを見てみます。
左ペインの真ん中に、現在の状態(トークンの配置)が書かれています。
右ペインのEventフィールドでは、実行可能なイベントが選べます。ここではpickup(x)
のみです。Parametersフィールドでは、x
に対する値を指定します。ここではB1
かB2
を選ぶことになります。
ではこの初期状態から、イベントにpickup(x)
、パラメタx
にB1
を選んで、Playをクリックしてみましょう。
モデル上でトークンが1つ、プレースholding
に遷移していることがわかります。アームにブロックB1がつかまれている状態です。
この状態では、イベントpickup(x)
だけでなくputdown(x)
も赤くなっており、実行可能であることがわかります。アームでつかんだブロックB1を下ろせるということです。
パネルの左を見ると、状態は「B2はテーブル上にあり、B1はアームにつかまれている」ことがわかります。
また右を見ると、イベントとしてpickup(x)
とputdown(x)
が選択できます。指定できるパラメタはそれぞれ、B2
・B1
です。
このように、描いたモデルに対しシミュレーションを実行して、想定外の動きにならないか、指定した状態に到達可能か、といった検証を行うことができます。
次回はこのモデルの問題点を直し、完成させていきましょう。