見出し画像

第136回: CFD法 その7(CFDからデシジョンテーブル 前編)

≡ はじめに

前回は、「CFD法 その6(Cause Flow Diagram)」と題し、複数の同値図をつないで、「仕様実現のための処理フローを図化する方法」について説明しました。

今回は作成したCFDからデシジョンテーブルを作る方法について説明します。ちょっと長くなってしまったので、前後編に分けます。(長いと読みにくいので)

私自身は、TVのコナンも前後編に分かれていると両方録画して一気に通して観るようなタイプです。
でも、削っても5千文字を超えてるnoteは普通は、読まれないと思うのです。
私も、そういう長いブログは『きっといいことが書いてあるんだろうな』と思いつつ高速スクロールしてしまいますし。だから、長くても5,000文字台にしています。
noteは、2,000文字台が望ましいと思っています。


≡ CFD法の前提(使いどころ)

前回、

CFD法が真価を発揮するのは、主に「統合テスト」ですが……。

と書きました。これには反論があるかもしれません。

手法は道具なのでどこに対しても使えると言えば使えます。また、自分が好きな手法はどのテストレベル・テストタイプでも使うことでしょう。それを否定するものではありません。
“真価を発揮する”と書いたのは、どこにでも使えるけれど、「CFD法を統合テストで使わないのは、もったいないよね」といった程度の意味です。

『ソフトウェア・テスト PRESS Vol.8』の「CFD法の極意(前編)」という、松尾谷徹氏(CFD法の考案者)の記事ではユニットテストを例に説明しています。

しかし、この記事には、次の記述があります。

以上、ユニットテストを例に、やさしいプログラムのテストについてCFD法の解説を行いました。ユニットテストの場合は、ここで示した方法を使わなくてもテストケースが増えますが、同値分割とそのマトリックスで対応できます。

同値分割までは同じだけれど、「ユニットテストでは、デシジョンテーブルではなくマトリックスで対応できる」ということです。

「このテストPRESSの記事ではユニットテストを取り上げたけれども、それは、“CFD法の解説のため”です。」
、、、と読み取りました。

松尾谷氏が、ここで書いている“マトリックス”とは、全組合せ(AC: All Combinations)のことです。仮に、2つのパラメータAとBがあり、それぞれの要素が{a1, a2, a3}と{b1, b2}だったとしたら、全組合せを網羅したテストをしたとしても、高々6つのテストケース(緑色のセル)しかありません。「それくらいなら全部やったら?」というわけです。

キャプチャ9

もちろん、パラメータの数が増えていけば、全組合せをテストするというアプローチでは、テストケースは指数関数的に増えますので破綻します。

それぞれのパラメータの要素が2つずつとしても、パラメータが10個あれば、2の10乗ですから1,024の組合せが存在します。
20個もあれば、1,048,576もの組合せ(=テストケース)になります。
ところで、そもそもユニットテストで
10個のパラメータの組合せをテストすることが
あるのでしょうか?

私は、チームメンバーが作ったソースコードをレビューしていて「10個の引数を持つ関数」をみたことがありません。(市販のライブラリでは見たことがありますし、お客様先の計測器のソフトウェアでパラメータを多く必要とするものでは引数自体は少なくても構造体になっていて、実質的に多くのパラメータを持つ関数もありました。そのような関数のユニットテストは結合テストの手法を用いました。この連載の数回先に書けるといいなあ。⚪︎◯)

ということで、関数の引数の数は、
5つくらいまでが適当ではないでしょうか?

とした場合、関数をテスト対象としたユニットテストのパラメータの数は高々5つということになります。それぞれの引数の要素の数が2なら、2の5乗で32テストケース、要素の数が3でも3^5=243テストケースです。このくらいのテストならデシジョンテーブルを作っている間に終わってしまうかもしれません。🙄

松尾谷氏は、以前、セミナーで、「パラメータ数が4個までなら、マトリックスにしてテストするほうがいいよ」と言っていました。

※ 4個までなら「2パラメータ×2パラメータ」のマトリクスを作ることで、マトリクスをディスプレイの1画面に詰め込むことで、視覚的に完成したマトリクスの不備のチェックがしやすいというメリットもあります。
GIHOZの「クラシフィケーションツリー法」では、[組合せ自動生成]ボタンを押したときに出てくるダイアログで「全組み合わせ」を選択できるようになりました。
ユニットテストにおける組合せテストは、クラシフィケーションツリーを作り、この機能を使って全組み合わせをテストすると良いと思います。ツールの使用をお勧めするのは、パラメータ数が少なくても同値分割は頭を使ってしっかり考えないといけませんし、全組み合わせを手作業で作ると間違えやすいからです。

※ 私はFL表を作って組合せを作るツールにかけていますが、一般的には上の方法が良いと思います。

このように、ユニットテストではマトリックスで全組合せをテストできます。しかし、統合テストではユニットをどんどん統合していくわけですから、いわゆる組合せ爆発が起こります。

各ユニットが2個のパラメータしかなくても、10個のユニットを統合したらパラメータの数は20となり、先程の計算の通りテストケースは、100万を超えます。自動化するとしても、これでは多すぎます。

一般に、テストを自動化することによって、多数のテストをテスト期間内に実行することができます。
ところが、1テストケースを0.1秒で実行できたとしても、100万テストケースを実行するのには、27時間もかかります。(そういうテストが有効なこともありますし、テスト環境を10個作れば3時間で終わると考えたら現実的なテスト(チェッキング?)でもあります)

そこで、以降に説明するCFDからデシジョンテーブルを作成するテクニックが役に立ちます。

「CFD法が真価を発揮するのは、統合テスト」と書いた理由は以上のとおりです。


≡ CFDからデシジョンテーブルを作成する方法

まずは、有効系と無効系の復習です。前回、

有効系は「その結果を使った処理が続くもの」、無効系は「そこで処理が終わるもの」

と書きました。実は、CFD法では、「有効系については組合せを考慮するが、無効系については組合せを考慮しないことでテストケースを減らす」という特徴があります。次のCFDを使って説明します。

キャプチャ15

こちらは、ジュース等の自動販売機の購入処理の流れを思い浮かべていただければと思います。流れ線を青色と赤色にしているのは、有効系(青色)と無効系(赤色)を明示したいからです。

ついでに、ズームイン・ズームアウトの補足をします。
以前、ズームイン・ズームアウトについて、

徐々に細かく分割していくことを松尾谷氏は「ズームイン」と呼びました。(分ける粒度を粗くする方は「ズームアウト」と言います)

と書きました。同値図を作成するときには、上記で作成してください。区分の原則4である「漸進の原則」(区分は順序通りに上位概念から下位概念に進むべきで、飛躍してはならない)を実践してほしいからです。

しかしながら、作成した同値図を流れ線でつないで、CFDを作成した後のズームイン・ズームアウトは、ちょっと違った使い方を指します。

CFDでのズームイン・ズームアウトは、
 ・ ズームイン = 要素まで表示する
 ・ ズームアウト = 同値クラス名まで表示する
とします。(ひとつずつの同値図はユニットテストが終わっている前提です)

ところで、同値図を描いていると、何がクラスで、何が要素なのか迷うことがあると思います。Suicaは、有効なカードという分類(区分肢)においては要素ですが、上のCFDではクラスになっています。それは、私がSuicaをさらに詳細化した「SuicaカードとモバイルSuica」を要素と考えているからです。
何がクラスで、何が要素かについては、テスト設計者が、テスト対象物を良く調べて決めるしかありません。だからクラシフィケーションツリーを描くのですね。当然、テスト設計者が間違えることだってあります。
テスト設計者が考え抜いて、それをみんなでレビューして納得のいくものに仕上げて、それでも間違えてしまったら、、、それはもう、組織の実力なのですからあきらめるしかありません。
※ 技法の良し悪しの問題ではありません。

クラスの粒度を決めるときのポイントとしては、「過去実績の有無」を重視します。
ほかには、たとえば、「SuicaカードとモバイルSuicaを一つの同値クラスとする」と決めたとしても、よりバグが出そうな方(例えば、“レアな携帯端末のモバイルSuica”)を使ってテストします。


脱線しました。CFDを再掲します。

キャプチャ15

このCFDからデシジョンテーブルを作ります。まずは、枠組みから作ります。こんな感じです。

キャプチャ12

CFDは、同値図(原因)と結果ノード(結果)を流れ線でつないだものですから、デシジョンテーブルの行は大きく「原因」と「結果」に分かれます。

原因の方は2列目のように同値図の数だけ分けて、それぞれをさらに、3列目のように同値クラスに分けます。CFDのデシジョンテーブル では、これ以上深く要因を体系化しません。肝心のルール列が見づらくなってしまいますので。

テストケース列は適当に多めに作っておきます。

全組み合わせをしても、
  カード(4)×残金と価格(3)×在庫(2)
  =4×3×2=24
ですので、24列あれば十分です。

この24をテスト空間(使用者が自由に設定できる組合せ条件)の大きさとして記憶しておきます。

ここでは、スペースの関係で12列にしています。慣れてくると「有効系の同値クラスの数の掛け算(3×2×1=6)」と「無効系の同値クラスの数の和(1+1+1=3)」を合計して、9列あれば十分といった感じで完成形を思い描くことができますが、とりあえずは、大きく作っておけば良いです。

逆に推奨出来ないのは“最初からxx件までしかテストしないぞ”と考えて、空のデシジョンテーブル を作るやり方です。
最終的にxx件しかテスト出来ないとしても、それが網羅基準に対して何%カバーしているのかの情報を示すことが大切だからです。ですから多めに列を用意します。

一番右側の列はセルの値の合計です。エクセルならsum関数をセルに埋めておきます。

デシジョンテーブルに組合せを埋めたものがこちらです。
※ セルに色を付けているのは説明のためです。

キャプチャ13

有効系についての全組合せが青色部分の6テストケースです。
無効系については、赤色部分の3テストケースです。無効系について、例えば最後の方の9列では、PasmoやEdyを使用したテストはしていません。つまり、たまたまSuicaを選んだだけであり、無効系のフローでは自分より前の同値図をどのような経路で通って来たのかを一切気にしません

整理すると、
有効系: 全組合せ
無効系: 無効系の同値クラス(各1テストケース)
です。

CFD法では、【無効系については組み合わせない】ことで、デシジョンテーブルのルール(テストケース)を減らしています。

CFDとデシジョンテーブルの関係を追いやすいように、CFDの流れ線にデシジョンテーブルの列番号を振ったものを載せます。

キャプチャ14

以下に、無効系を減らしてよい理由について説明します。

無効系とは、自販機の例でいえば、「自販機が対応していないカード」、「カードの残金不足」、「商品の在庫がない」場合です。いずれも「購入不可」という無効な結果となるものです。

無効な結果は、自販機の利用者にとって、望まない結果です。利用者はジュースが欲しくて自販機を利用するのです。

したがって、有効な結果をもたらす条件については組合せまで手厚くテストをして、無効な結果をもたらす条件については優先度を落とすというアプローチは、トラブル発生時のリスクに対して合理的です。

さらに付け加えると、無効な結果は一つの同値図内の処理によって決まることがほとんどです。したがって、それ以前のフローを無視しても大丈夫なことが多いものです。


≡ CFD法が網羅しているもの

今回、CFDからデシジョンテーブル を作成しました。あとは、デシジョンテーブル を一列ずつテストしていけば良いということになります。

ここで、見出しにある「CFD法が網羅しているもの」について考えてみます。

まず、結果ノードですが、こちらはテスト対象の結果を同値分割したものになっています。あまり意識していなかったと思いますが、『ソフトウェア・テストPRESS Vol.8』の記事でも、CFD法の手順として、

① 機能(結果)の同値分割
 入力:仕様書
 出力:結果の同値分割図(CFDの要素)

と書いてあります。

「結果の同値分割図」って何? と思われた人がいると思います。テストPRESSの記事では結果ノードではなく、結果についても同値図(正しくは同値分割図)で描くようにと書いてあります。

今回のnoteで、結果について「結果ノード」にするか「同値図」にするか迷いました。昔は結果ノードだったのですが、いつの頃からか(2007年あたり?)、「1番はじめに、仕様書から結果を同値分割して結果の同値分割図を描きなさい」と言われるようになりました。
それは全くその通りなのですが、結果の同値分割は案外難しいので、このnoteでは、もっと易しく、フローの出口を結果ノードとして表しています。(次回のnoteでは、参考のため結果の同値分割図で描きますね。)

それで、「機能(結果)の同値分割」ですから、結果ノードについて1度でもいいから確認したらそれは、その機能を網羅したことになります。

少なくとも、その機能は実装されていて、
動作したというエビデンスを残せます。

※ 条件を変えたらバグが見つかるかもしれないけれど。
デシジョンテーブル の結果行の各行について合計列がゼロで無いことで機能を網羅していることが確認できます。

CFD法でテストした後に、機能網羅していないことが分かった場合は、結果ノードの描き忘れと言うことです。

記事にある、2番目の手順は、

② 入力(原因)の同値分割
 入力:仕様書
 出力:原因の同値分割図(CFDの要素)

です。このnoteでは、“同値図”と表記していました。口頭では同値図と呼ぶことが多いからです。正しくは“同値分割図”ですが、どちらで呼んでも良いと思います。

レベッカ(Rebecca)の愛称がベッキー(Becky, Beckii)のようなものです。

こちらは、入力を同値クラスの粒度で網羅したことになります。特に有効系については全組合せ網羅を説明しました。

デシジョンテーブル の原因行の各行について合計列がゼロで無いことで入力を網羅していることが確認できます。

実際のところは、有効系について全組合せをすると、組合せ爆発が起こります。その対処方法については次回説明します。


≡ おわりに

今回は、CFDからデシジョンテーブルを作る方法について説明しました。次回は、同値図がたくさんあった場合の「有効系の減らし方」について説明します。また、「同値クラスの関係(ANDとOR)とデシジョンテーブル」についても書く予定です。

いいなと思ったら応援しよう!