先日の記事で、状態遷移テストのカバレッジについて書きました。
この記事のために利用させていただいている、モデリングツール「astah*」。このastah*で描く状態遷移表に用意されている、ignore と not happen という選択肢について、astah*内での位置づけと、テストの要否について考えてみました。
なお本記事は、Twitterにおける秋山さんとの以下のスレッドをベースにしています。
例えば電卓で1を押し続けたときに、それを無視するのと「111…」とイベントを処理して同じ状態に戻るのとは違います。
— あきやま🐾 🐾 (@akiyama924) 2020年5月6日
後者についてNスイッチでテストするとカバレッジ率が上がることは当たり前と思うのですが、前者(1を押し続けても初めの1しか受け付けず捨てる)のときどうなるのかなと思いました。
ignore と not happen
前回の記事に書いた通り、ignore と cannot happen はおおむね、以下のような意味を持っています。
- ignore: 当該イベントが発生しても、発生しなかったように扱う。
- cannot happen: 当該イベントは発生しえない。
似ているように見えますが、前者は「イベントが発生しうる」、後者は「イベントが発生しえない」ので、根本的に違いますね。
例を挙げて説明してみます。
会社なんかでよくある「カードタッチで解錠するドア」を考えましょう。ざっくりこんな仕様。
- 内側からは、タッチなしで開けられる。
- 外側からは、タッチで解錠したうえで開けられる。
- ドアが開いたまま、最後のタッチから一定時間経つとアラームが鳴動する。
- アラームは、カードタッチすることで停止する。
- アラーム鳴動中はデッドボルトが飛び出しており、そのままドアを閉めることができない。アラームを止めてデッドボルトが引っ込んだ状態にしたうえで閉じる必要がある。
5個目の長くてわかりづらくて不自然な仕様は、説明のためだと思って受け入れてください。平行線公準のようなものです。
ステートマシン図は以下のようになります*1。
astah*で自動生成してくれた状態遷移表は、以下の紫セル。
白セルは、個別に埋めていく必要があります。ここに書かれた ignore と cannot happen の意味を考えてみましょう。
ignore と自己遷移
ignore は2つのセルに書かれています。
- ドアが施錠された状態で、外からドアを開ける。
- アラームが鳴動している状態で、外からドアを開ける。
これらのケースでは状態が遷移しないだけでなく、何も起きません。
一方、以下のセルはどうでしょう。
- ドアが開いた状態で、カードタッチする。
- 「ドア開&鳴動なし」×「カードタッチ」→「ドア開&鳴動なし」
これも同じ状態に留まりますが、アラームが鳴るまでの時間経過をリセットするアクションが内部で動いていると考えられるため、「同じ状態に遷移した」と解釈するのが妥当でしょう。
cannot happen の区別
cannot happen はけっこう出現していますね。
たとえば以下の2つは、まさに「起こりえない」イベントといえます。
- ドアが開いている状態で、外からドアを開ける。
- ドアが閉じた状態で、ドアを閉じる。
一方、以下はどうでしょうか。
- アラーム鳴動中に、ドアを閉める。
これは確かに「仕様上は起こりえない」ことになっていますが、原理的に「起こりえない」イベントではありません。実装が間違っていて、仕様通りにデッドボルトが飛び出さなかったら、「閉じ」れてしまう可能性があります。この種のイベントは、cannot happen というよりは、should not happen とでも扱うのいいのかもしれません。ともあれ、2つを区別する必要があるでしょう。
状態遷移表のセルのパターン
以上の話を整理すると、以下の5つのパターンに分けられます。
- イベントが発生すると、別の状態へ遷移する。(遷移)
- イベントが発生すると何かが起きるが、同じ状態にとどまる。(自己遷移)
- イベントが発生しても何も起きず、同じ状態にとどまる。(無視 = ignore)
- イベントが発生しないことになっている。(仕様的不可 ∈ cannot happen)
- イベントが原理的に発生しない。(原理的不可 ∈ cannot happen)
カッコの中の日本語は、ここで暫定的に付けた名前です。ちゃんとした呼称があるかも。
ではこの5つのうち、状態遷移テストで検証すべきものはどれでしょうか。
a.とb.は「遷移」そのものなので、当然確認する必要があります。e.は起こすことができないのですから、そもそも検証不可能です。
c.はどうでしょう。イベントを起こすことができるので、「本当に無視するか」を確認する必要がありますね。
d.も考え方は同じです。「本当にイベントを起こせないか」を確認する必要があります。
カバレッジで網羅されるパターン
astah*のプラグインで導出される状態遷移パスを確認してみましょう。
導出された「遷移回数=1」の13ケースの遷移に、「無視」パターンも含まれていることがわかります。「遷移回数=2」でも同様で、ignore が2回続く(そして2回とも無視される)パターンも含まれています。
一方で、cannot happen については、「仕様的不可」と「原理的不可」の区別がないため、起こりえない=テストできない とされ、テストケースに含まれません。
よってスイッチカバレッジのテストを行う際には、「起こりえない」がどちらのパターンなのかを峻別したうえで、「仕様的不可」については「仕様通りに動作する(イベントが発生しない)こと」を個別に確認しなければなりません。
*1:ツッコミどころはあるかもしれない。ドア開での「一定時間超過」とドア閉&解錠での「一定時間超過」を同じイベントとして扱っていいのか、とか。