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

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

コープランド本の例題から、ブラックボックステストとホワイトボックステストを考える

 プレスマンの白本*1・「第19章 ソフトウェアテスト - コンポーネントレベル」の「19.5 ブラックボックステスト」には、以下のような説明があります。

ブラックボックステストテストはホワイトボックステストの代替ではない。むしろ、ホワイトボックステストの手法でみつかるのとはまた別の種類のエラーを見つけることを期待できる、補完的なアプローチと言える。

 これを確認するために、『はじめて学ぶソフトウェアのテスト技法』(リー・Copeland著、日経BP社)の例題を見てみたいと思います*2

Binderの例題

 同書第1章の節「すべてをテストすることはできない」では、その例を以下のようなプログラム*3で示しています。
 関数blech*4は整数iを受け取って1を引き、30,000で割っています。余りなしで「商」を求める*5ものです。

gist.github.com

 ここで、本来は「1を足す」が正しい処理だったとします。その場合、この欠陥はどのように見つけられるでしょうか。
 なおここでは、intの最小が -32,768、最大が 32,767 という前提です。つまり整数だけ考えても、65,535 65,536の入力候補があることになります。

ホワイトボックステスト

 たとえば i = 1 の場合、正しいコードでも間違ったコードでも出力は0となり、間違いを検出できません。
 が、ホワイトボックステストの制御フローテストを考えた場合、i =1 を実行すれば、ステートメントカバレッジは100%となります*6

ブラックボックステスト

 それでは、ブラックボックステストではどうでしょう。
 ブラックボックステストを行う前提として、以下のような仕様が明示されていたとします。そのままプログラムみたいなものなので、現実には考えられないでしょうが・・・。

入力された整数に1を足したあと、30,000で割った商。

 テスト技法として、同値分割と境界値分析を使ってみます。
 まず、入力値の同値分割。
 1つの切り口として、整数iの領域を、正・ゼロ・負の3つのエリアに分割することが考えられます。その境界値は下から、intの下限、-1、0、1、intの上限という5つになります。

 次に出力値を考えてみましょう。
 結果となる商の領域も、正・ゼロ・負の3つのエリアに分割できます。商が-1から0になる境界と、0から1になる境界を考えると、入力値は -2、-1、29998、29999 という4つになります。

誤りを検出できる入力

 両者から重複を除くいた8つのテストケースについて、正しいプログラムと間違ったプログラムでそれぞれどのような出力になるかを整理したのが以下の表です。3つのテスト(i = -1, 0, 29999)で、誤りを検出(★)できていることがわかります。

i 期待する結果 実際の結果 検出 同値分割 (入力) 同値分割 (出力)
-32768 -2 -2
-30001 -1 -2
-30000 -1 -2
-2 -1 -1
-1 0 -1
0 0 -1
1 0 0
29998 0 0
29999 1 0
30000 1 0
32767 1 1

結論

  • ホワイトボックスでコードカバレッジ100%を達成したというのは、そのパスを通す一例が実行できたということに過ぎず、十分なテストをしたことを意味するわけではない。
  • 仕様に基づくブラックボックステストは、ホワイトボックステストでは満たせない部分をカバーすることができる。

気になること

 Copelandさんのこの節で、よく理解できない点が2つあります。

  1. 入力の例として、1、42*7以外に、intの範囲を超えた40,000、-640,000を挙げている。「期待される結果」「実際の結果」も合っていない。

  2. 例題において、欠陥を検出できる入力は4件としているが、上の表にも示すように、6つの入力値で出力の差が出る。

 わたしの方が間違っているのかもしれませんが、誰かこの謎を解いてください・・・。

Cat eyes on black body"Cat eyes on black body" by Stefan Tell is licensed under CC BY 2.0

2022/3/30追記

 「気になること」について、辰巳さんにが原著や引用元をあたってくださり、概ね以下のことが判明しました。ありがとうございます!

事実1 引用・改版・翻訳で情報が変化

 Copeland本ではこの例を、Binderの本からの引用としていますが、これはさらにFriedman*8の本からの引用であるとのこと。
 またCopeland本の翻訳の過程で数字が(おそらくは意図せず)変わってしまったり、原著の方ではErrattaが出ているなど、間違いが何箇所かで紛れ込んでいるようです。

事実2 入力例に誤植あり

 入力値-640,000というのは、本書が前提としているintの下限以下であり、例としてはおかしいと考えていました。この点、原著では-64,000だったとのこと。
 ただ、-64,000も下限以下なんだよな。。

事実3 負の数の除算の扱い

 -7/3 = -2.333... の小数部を切り上げて-2にするか、切り下げて-3にするかについて、Pythonの //演算子では切り下げになっており、また割り算の定義もこれに合致するため、この記事では切り下げを採用しています。
 が、Copeland本では切り上げを採用しているようです。

 切り下げと切り上げでは、欠陥を検出できる入力値も個数も変わってきます。

  • 切り上げ(ExcelのROUNDDOWN()イメージ): -30000 -29999 29999 30000 の4つ
  • 切り下げ(ExcelのINT()イメージ): -30001 -30000 -1 0 29999 30000 の6つ

 Copeland本でいう「4つ」というのは、この結果だと思われます。

事実4 Binder本では「40個」

 Copeland本の引用元となるBinder本では、欠陥を検出できる入力値は40個であるとしているとのこと。これは、割る数が30,000ではなく3,000となっているからのようです。
 割る数を3,000とし、また負の数の除算を切り上げで考えると、確かに40の入力値で差分が出ることが確認できました。

 一部のマニアしか興味をもたないと思いますが、わたしは好きなので残しておきます。将来Copeland本をマジメに読んで「!?」となる人のためにはなるかもしれません。

参考

 不定期に盛り上がる同値分割、境界値分析に関するTogetterを参考にしました。
 読み直して、今持ってなお自分が、基本にして最難関のこの技法を十分に理解できていないことを感じました。

*1:『実践ソフトウェア・エンジニアリング 第9版』のことです。

*2:正直、この記事の内容には自分自身でも疑いを抱いています・・・。

*3:Robert Binderの『Testing Object-Oriented Systems』からの引用とのこと。

*4:英辞郎によるとblechは

間投 《嫌悪》ゲーッ、オエッ、ゲッ、くそっ、ウヘッ、ヒェッ

*5:負の整数の割り算について、 -7÷3 = -2...-1 ではなく、-7÷3 = -3...2 という計算をするようです。 定義上、0≦余り<割る数 と考えるため。

*6:ブランチカバレッジも100%です。そもそも分岐がないので。

*7:42は、生命、宇宙、そして万物についての究極の疑問の答えを表すため、代表値として選択する価値がある。ja.wikipedia.org

*8:以下、教えていただきました。
M. A. Friedman and J. M. Voas. Software assessment: reliability, safety, testability. 1995.