見出し画像

91号:境界値分析: 前編

≡ はじめに

ASTERセミナー標準テキスト」の97ページあたりについてです。

前回は、同値分割法のテクニックについて書きました。今回は同値分割法の拡張技法である境界値分析について書きたいと思います。


≡ 境界値分析の難しさ

以前、境界値分析をJSTQBのFLシラバスを引用し、次のように紹介しました。

同値分割法の拡張で、パーティションが数値または順序付け可能な値で構成される場合に、パーティションの最小値と最大値(または最初の値と最後の値)を選んでテストする。

同値分割法によって境界値分析が正しく見つかれば、あとは、その要素に順序付けが可能であれば、最小値と最大値をテストするだけなので、境界値分析は機械的にできそうです。機械的にできるということは頭を使う必要が無いということで、つまりは簡単な*はず*です。

実際に境界値分析そのものは、最小値と最大値を見つけるだけですから簡単です。

しかし、不思議と同値分割法よりも境界値分析の方が上等なテスト技法っぽく感じます。

しかも、リリース後に「境界値がらみのバグ」が見つかることが多く、「きちんと境界値分析したのか!?」と、まるでテスト技法の勉強不足による失敗があったかのように注意を受けることがあります。
いう方に悪気はなくても、正しく原因を指摘しなければ、問題は直りませんので言わないか、「原因分析をして再発防止しなさい」と注意するにとどめてほしいものです。

多くのケースでは、境界値分析が難しかったのではなく、「同値分割時に境界値の仕様について深く分析せずにすませた」ことや「設計や実装時に意図せずに発生した“仕様には存在しない境界値”」が失敗の原因です。
なぜ、同値分割時に境界値の仕様について深く分析せずに済ませてしまったのでしょうか? その理由は同値分割法では平均値や中央値や最頻値をテストすれば良いために、最小値と最大値について厳密に考える必要が無いためです。

実生活でも、同様です。
例えばジェットコースターの乗車条件に「身長130cm以上」と書いてあったとします。このとき、身長129㎝の子供がちょっと背伸びして誤魔化していたらどうしますか? おそらく見て見ぬふりをするのではないでしょうか。(あさには130㎝あったかもしれないですし、健康そうに見えますし……。)
いわゆる“あそび”や“余裕”部分が無いと、ものごとはスムーズに進みません。
「ちょっと待って。背伸びして、ずるしていない?」と止めることがいつも正しいとは限りません。

他の例ですが、2時間1000円(以降は1時間300円)の駐車場があったときに、1分でも超えたら1300円か? というと、多くの場合そんなことはありません。なぜなら、そんなことをしたら駐車場の出口渋滞で料金ゲートを出るまでに時間が掛かったときにクレームになりかねないからです。
15時までのランチタイムのレストランで、14:30に入ったお客様に対して、「15時までに食べ終えろ」というでしょうか? 「ごゆっくりどうぞ」というのではないでしょうか??
カラオケは? 居酒屋は??

実生活(特に、客商売)では厳密に境界を扱うことはぜずに、お客様に有利なように融通を利かせるものです。「運用で回避」は嫌われがちな要求ですが……。

もちろん、左右にふらつく運転をしていたドライバーに対する酒気帯び検査や、支払時の1円の過不足や、コロナ禍における入館時の検温など、「安全」や「財産」や「健康」に関する基準は法律等で決められ、みんなで厳守します。また、70点以上が合格の試験の合否判定ソフトウェアは、69点は不合格として、70点は合格にしないといけません。

そのようなソフトウェアとなっていることをテストで確認するために境界値分析を用います。

境界値分析は、最小値と最大値についてテストするわけですから、最小値と最大値について厳密に見極める必要があります。そこが難しいです。

これを「同値分割法が先送りしてしまった最小値と最大値についての検討を、境界値分析時に尻拭いしていると見ることもできます。
しかし、遡れば、そもそも「最小値と最大値は、開発の詳細設計やプログラミング時に必要な情報」なのですから、「要求仕様を作成するときにしっかりと仕様について分析し、要求仕様レビュー時に、最小値と最大値について(根拠が示されていないなどの)問題があれば指摘し修正する」ことが根本的対策となります。

ところで、要求仕様の作成時のタイミングでは最小値と最大値について決定できないものもあります。
しかし、「最小値と最大値について、“確定値”かそれとも“暫定値”か。暫定値ならいつのタイミングで確定するのか」について要求仕様書のフィックス(公式文書になる)までに決めて、暫定値については、それを管理対象としてフォローすることが必要です。
また、なかには、「最大値についてはリソースが許す限り」という仕様にすることもあります。例えば、「メモリーを増やせば上限も増える」というわけです。この場合も、「リソースXXがYYの値なら上限はZZ」というように、リソースと上限値の関係を設計情報として明らかにしてほしいものです(それが明らかになっていればテストも楽できます)。

メモリでいえば、1バイト単位の厳密さは不要ですが、1MB単位くらいではテストしたいところです。

上記とは別に、要求自体がよく分からずに、開発者が(勘違いをして)誤った仕様を書いてしまうことがあります。簡単な例を挙げます。

「今度の研修は来週の、火曜日から金曜日までです」というときの研修実施日の同値パーティションは(来週の){火曜日、水曜日、木曜日、金曜日}です。したがって境界値分析を行って求めた境界値(テストすべき値)は「火曜日と金曜日」です。簡単ですね。

次に「東海道新幹線の“のぞみ号”は新横浜駅を出ると名古屋駅まで停車しません」というときの停車しない駅の同値パーティションを考えてみます。“こだま”が止まって“のぞみ”が止まらない駅は{小田原、熱海、三島、新富士、静岡、掛川、浜松、豊橋、三河安城}です。したがって境界値分析を行い求めた境界値(テストする値)は「小田原と三河安城」です。こちらも簡単ですね。

いや、よく見てください。「金曜日まで」、「名古屋駅まで」、、、同じ「まで」なのに金曜日は同値パーティションに含まれて、名古屋は含まれていません!

「まで」については『練習帳』のコラムにも書かれていますが、なかなか厄介です。いくつか列挙してみます。

≪含む場合≫
 ・ 火曜日から金曜日まで
 ・ お勧めメニューの端から端まで

≪含まない場合≫
 ・ 名古屋まで止まらない
 ・ 暗くなるまでに帰ってきなさい

≪よく分からない場合≫
 ・ 会議時間は13時から15時まで
 ・ 入学までに自分の名前くらいは書けるようになってほしい

上では、「まで」について書きましたが、実は「以上」、「以下」も危険な言葉です。なぜなら日本では「3以上」といえば、3を含むと決まっていますが、中国語で「3以上」というと、3を含まない用法もあるからです。(下記の画像は中国語-日本語辞書を引いた結果です)
「以下」も同様です。中国の方にテストをお願いするときには、事前に「その値を含むのかどうか」について確認しておいた方が良いかもしれません。

画像1

境界値はコンピュータの実装上も悩ましいことがあります。
たとえば、カレンダーの「丸1日」を指定するときに、「00:00:00から00:00:00」として、「0時以降、翌日の0時未満」と実装してあるスケジュール管理アプリケーションAがあってもよいです。そのアプリケーションは全く問題なく機能することでしょう。また、別のアプリケーションBでは丸1日を指定するときに、「00:00:00から23:59:59」として、「0時以降、当日の23時59分59秒以下」と実装してあってもよいです。そちらも全く問題なく機能することでしょう。
でも、このとき、アプリケーションAが書き出したスケジュールデータをアプリケーションBで読み込んだら変なことが起こるかもしれません。

Bの方で読み込んだら、「丸1日」ではなく「丸2日」となったケースがありました。
今はiCalendarという標準形式(RFC 5545)でやりとりされることが多いのでカレンダーのデータ移行互換性問題はほとんど起こらなくなりました。

このように境界値のあたりは、霧につつまれて、もやっとしていることが多いものです。したがって、霧の中を運転するときに、スピードを落として、センターラインを超えたり、渋滞の最後尾の車に突っ込まないように気を付けるのと同じように、境界値についてもテスト設計のスピードを落として慎重にテストを作る必要があります。

それでは、そんな境界値分析をどのように行うのか学んでいきましょう。


≡ 無効同値パーティションはどこまで……

以前、例に挙げた「コンビニでお酒を買おうとしたときの年齢確認タッチパネル」を例にして考えてみます。

年齢について、「20歳未満」と「20歳以上」の2つのケースをテストしたら問題ない。と書きました。

前々回と前回、同値分割法を学びましたので、年齢を以下の2つの同値パーティションに分けてみます。
 ① 20歳未満: 0歳、1歳、2歳、、、17歳、18歳、19歳
 ② 20歳以上: 20歳、21歳、22歳、、、137歳、138歳、139歳

同値分割法は、上記の2つの同値パーティションから代表値をテストするテスト技法ですから、①の「20歳未満」の同値パーティションから例えば“15歳”をテストし、②の「20歳以上」の同値パーティションから例えば“30歳”をテストします。先に書いた「2つのケースをテストしたら問題ない」と書いた2つのケースは、“15歳”と“30歳”ということです。(もちろん、“17歳”と“42歳”でも構いません)

境界値分析は、「パーティションが数値または順序付け可能な値で構成される場合に、パーティションの最小値と最大値(または最初の値と最後の値)を選んでテストする」テストですから、境界値分析をしてみると、それぞれの同値パーティションの境界値(最小値と最大値)として、
 ① 20歳未満: 0歳と、19歳
 ② 20歳以上: 20歳と、139歳

が見つかります。テスト回数は2回から4回に増えてしまいますが、境界が正しく実装されているかを確認するために効果的なテストです。

例えば、年齢(変数ageを使用)が20歳以上を処理を
  if ( 20 < age) {xxx;}
のように「<=」と書くべきところについて「<」と誤って書いてしまいますと、成年の判定から20歳が抜けます。このような欠陥を見つけるのに境界値分析法で作ったテストケース(この場合なら“19歳”と“20歳”)が役に立ちます。

ところで、前回の最後に無効同値パーティションについて考えました。

「コンポーネントまたはシステムが拒否する値の集まり」を無効同値パーティションと呼びます。有効同値パーティションの外側には無効同値パーティションが2つあります。最小値よりも小さな無効同値パーティションと、最大値よりも大きな無効同値パーティションがあるからです。(詳しくはMyersの『ソフトウェア・テストの技法』、あるいは居駒さんが整理した文章を参照ください)
これを考慮して、2度目の境界値分析をすると、
 ⓪  0歳未満: WW歳と、-1歳  無効同値パーティション
 ①  20歳未満: 0歳と、19歳  有効同値パーティション
 ②  20歳以上: 20歳と、139歳  有効同値パーティション
 ③ 140歳以上: 140歳と、XX歳  無効同値パーティション

となります。
さて、WW歳とXX歳は、それぞれ何歳をテストするのでしょう?

WW歳とXX歳には、「プログラム上、年齢(age)を格納する変数の取りうる範囲を入れる」ことを勧めている本もあります。
例えば、年齢を格納する変数の型が符号付16ビット整数(signed short int)の場合、その変数に格納できる整数の値は、-32768~32767です。
このことから、WW歳は-32768歳、XX歳は32767歳というわけです。
(仮に、16ビットでなく、8ビットの型を選んでしまうと、符号付8ビット整数の値は、-128~127ですから139歳を変数に格納できません。、、、と言った問題を見つけることができるかもしれません)

2度目の境界値分析結果を書き直すとともに番号を①から振り直します。(3度目の境界値分析)
 ①  0歳未満: ー32768歳と、-1歳  無効同値パーティション
 ②  20歳未満: 0歳と、19歳  有効同値パーティション
 ③  20歳以上: 20歳と、139歳  有効同値パーティション
 ④ 140歳以上: 140歳と、32767歳  無効同値パーティション

ここまでくると、「変数の範囲を超えた無効同値パーティション」について気になります。つまり、4度目の境界値分析結果は、
 ⓪ 変数の最小値未満: YY歳と、-32769歳  無効同値パーティション
 ①  0歳未満: ー32768歳と、-1歳  無効同値パーティション
 ②  20歳未満: 0歳と、19歳  有効同値パーティション
 ③  20歳以上: 20歳と、139歳  有効同値パーティション
 ④ 140歳以上: 140歳と、32767歳  無効同値パーティション
 ⑤ 変数の最大値超: 32768歳と、ZZ歳  無効同値パーティション

となるのではないかという点です。

ここで、YY歳とZZ歳について、例えば、符号付32ビット整数の取りうる範囲(-2,147,483,648~2,147,483,647)を使って、YY歳は-2,147,483,648歳、ZZ歳は2,147,483,647歳としたとしても、さらに、「その外側は分析しなくて良いの?」となりますので、切りがありません。5度目の境界値分析は、したほうが良いのでしょうか? するとしたときには、どの値をテストすべきでしょうか??


≡ プラクティカルな境界値分析

結論から言えば、境界値分析で実施するテストは、無効同値クラスを考慮して、-1歳,0歳,19歳,20歳,139歳,140歳をしておけば十分です。2度目の境界値分析結果を再掲します。

 ⓪  0歳未満: WW歳と、-1歳  無効同値パーティション
 ①  20歳未満: 0歳と、19歳  有効同値パーティション
 ②  20歳以上: 20歳と、139歳  有効同値パーティション
 ③ 140歳以上: 140歳と、XX歳  無効同値パーティション

こちらのリストから、数値が入っている値をテストします。
(「常に2度目の境界値分析で十分」ということではありません。テストの7原則の6より「テストは状況次第」ですから、状況によっては3度目の境界値分析結果を使うことがあるかもしれません)

では、どうやって、上記、-1歳,0歳,19歳,20歳,139歳,140歳を導くのでしょうか。毎回、人によって結果が変わるのはよろしくありませんので、以下に私が行っている手順で説明します。


① 境界値分析が可能であることを確認する

「パーティションが数値または順序付け可能な値で構成される場合」にのみ境界値分析が使えます。入力パラメータ(上記なら年齢)についてその値を何かの視点で並べることが出来るか確認します。
パラメータはプログラミング時には数値変数として実装されることが多いですから(数値変数ではない場合もあります)、判断に迷ったら、ソースコードを見てしまうのも手かもしれません。


② 有効は無効に挟まれる

有効同値パーティションは連続するかもしれませんが、その最小値より小さい、もしくは、最大値より大きい値からなる無効同値パーティションに挟まれます。

 ⓪  0歳未満: WW歳と、-1歳  無効同値パーティション
 ①  20歳未満: 0歳と、19歳  有効同値パーティション
 ②  20歳以上: 20歳と、139歳  有効同値パーティション
 ③ 140歳以上: 140歳と、XX歳  無効同値パーティション

今回で言えば、最小値より小さい値からなる無効同値パーティションは⓪で、最大値より大きい値からなる無効同値パーティションは③です。
「無・有・無」が基本の型です。こちらの延長で、例えば、
    「無・有・無・有・有・有・無・無・有・有・無・無」
となることもあるかもしれません。いずれにしても、両端は無効同値パーティションです。


③ 全ての有効同値パーティションの「最小値ー1、最小値、最大値、最大値+1」をテストする

今回で言えば、①と②が有効同値パーティションですから、それぞれに対して「最小値ー1、最小値、最大値、最大値+1」をテストします。
※ 「ー1」と「+1」は「直前」と「直後」の意味で使ってください。整数でない場合もあり、その場合は1を足し引きするのではなく、最小値と最大値に最も隣接するギリギリの値を使ってください。(最小値が24.0なら23.9のように。23.99がテストできるなら23.99で)

 ①  20歳未満: -1歳、0歳、19歳、20歳
 ②  20歳以上: 19歳、20歳、139歳、140歳

です。最小値と最大値のそれぞれに対して2つずつテストするため、これを「2ポイント境界値分析」と呼ぶことがあります。テスト実装時には、重複する19歳と20歳については一つにまとめて、1度ずつテストをしますが、テスト設計の網羅基準としてはあくまでも「2ポイント境界値」であることに注意してください。このテストの網羅度を測る時の分母は、「有効同値パーティションの個数 × 4」です。

※ これで、無効同値パーティションの境界について必要最小限の値をテストできます。


≡ 終わりに

今回は、同値分割法の拡張技法である境界値分析について書きました。
短くまとめる予定だったのですが、今回も長文になってしまいました。そこで「前編」としたのですが、それでも長いですね。

次回は、後編として、「3ポイント境界値分析」について書きたいと思います。

この記事が気に入ったらサポートをしてみませんか?