更新日時: $Date: 2003/06/26 05:13:45 $

[ 目次 ] [ まえがき ]
[ 第1章 ] [ 第2章 ] [ 第3章 ] [ 第4章 ] [ 第5章 ] [ 付録A ] [ 付録B ] [ 付録C ]
■■ [1.1] ■■ [1.2] ■■ [1.3] ■■ [1.4]

■■1.1 テストって何?

一般の人がテストと聞くとまず思い出すのは、小学校くらいから長々とお世話になってきた定期試験のようなものではないかと思います。それとともに嫌な記憶も蘇るかもしれません。テストが好きで好きでたまらなかった人は稀だと思います。

本書でいうところの「テスト」は、このように人に対するものではなく、プログラムに対するテストのことです。厳密にいうときには「ソフトウェア・テスト」という用語を使います。プログラムに対するテストでも、自分の作ったものに対するテストは、やっぱり微妙に嫌なものだったりするのですが、その話はまた後にしましょう。

また、この本でいう「テスト」は、一般的な意味の「(ソフトウェア・)テスト」とは少し異なります。この節では、この本で使用する「テスト」の概念について明確にして、あとの説明をわかりやすくすることを目指します。しばらく、お付き合いください。

■■■1.1.1 一般的なソフトウェア・テスト

何かを理解しようとするとき、その内部がどのように分類できるかを見ていくことは役に立ちます。本節では「ソフトウェア・テスト」がどういうものか理解するために、「ソフトウェア・テストはどのように分類できるのか」見ていきたいと思います。また、その中で本書で扱うテストがどのあたりと対応するのか述べてみたいと思います。

この本で紹介する分類は“ソフトウェア・テストの技法[1]”、“基本から学ぶソフトウェアテスト[2]”、“ソフトウェア工学[3]”などの文献を基に、著者が個人的に再構成したものです。ですので、もしかしたら間違いがあるかもしれません。疑問がある方は、これらの本と、これらの本の参考文献をあたると良いでしょう。

■■■■プログラムを動かすかどうか

第1番目の分類基準は「テスト対象のプログラムを動かしてテストするかどうか」ということです。動かさずに行うテストを「静的テスト」、動かして行うテストのことを「動的テスト」といいます。

○静的テスト

静的テストでは、プログラムを動かさずにテストします。つまり、コンピュータの代わりに人やチェックプログラムが、プログラムの動作を追って異常を見つけます。

人がおこなう場合には、個人がおこなうテストと、組織的におこなうテストに分けられます。個人が行うテストには机上デバッグや机上テストといったものが、組織的に行うテストにはウォークスルー(Walk Through)、インスペクション(Inspection)、コードレビュー(Code Review)などがあります。一般に「自分のプログラミングにミスがあるとは考えたくない」という心理障壁により、バグから目をそらしてしまうことがあります。このため、テストはプログラムした人以外がするべきだとされています。

チェックプログラムには専用のものもありますが、コンパイラやリンカも、それぞれ構文チェック、ライブラリのチェックのツールといえなくもありません。

○動的テスト

動的テストでは、実際にプログラムを動作させてチェックします。動的テストには様々な側面があり、以下で述べる分類は、動的テストに対するものです。

■■■■内部仕様と外部仕様

動的テストの分類基準の一つとして、「ホワイトボックステスト」と「ブラックボックステスト」、という分け方があります。ホワイトボックステストでは、「プログラムがどのように動いているのか(HOW)」という内部仕様をテストします。一方、ブラックボックステストでは「プログラムはどうあるべきなのか(WHAT)」という外部仕様をテストします。開発現場では、どちらのテストも重要です。

○ブラックボックステスト

ブラックボックステストでは、プログラムを「中身の見えない箱=ブラックボックス」としてテストします。一般的には、プログラム全体をチェックすることをいいますが、本書では、プログラムではなく、モジュールをブラックボックスとして取り扱う場合にも使用します。なお、ここでいう「モジュール」というのは、機能や概念で分割されたプログラムの一部のことです。モジュールを組み合わせると、1つのプログラムになります【図1.1.1】。

【図1.1.1】モジュールが組み合わさった図

モジュールが組み合わさった図

ブラックボックステストでは「そのプログラムがどうあるべきか(外部仕様)」という視点からプログラムをチェックします。具体的には、事前の状態に対して、実行結果が予測された状態になっているかをチェックします。テスト対象とするプログラムの機能やインターフェースに着目するため、機能テストともいいます。しかし、プログラムの中身が見えないので、コーディングミスの修正など、実際の作業にはあまり役に立ちません。ブラックボックステストの細かい技法については、後で説明します。

ちょっとひと息※閑話休題は意味が違うとのご指摘有り。多謝>山口さま

「インターフェース」という言葉が難しいという人がたまにいますが、語源を知っていると分かりやすいかと思います。インターフェース(interface)の語源はface(顔・表面)のinter(相互の・間)つまり、何かものとものの表面の間の触れ合う部分のことです。

モジュールとモジュールが触れ合っている、つまりひとつのモジュールが相手の関数を呼んでいるときには、その関数がインターフェースになります。人間がコンピュータと触れ合う、つまり文字入力するときには、キーボードがインターフェースですし、コンピュータが人間に向かって画像などを表示する場合には、その画面がインターフェースということになります。

それで、ユーザとやりとりをする部分=ユーザインターフェースをグラフィカルに作ると、GUI(グラフィカル・ユーザ・インターフェース)、コマンドモードなどでキャラクタでやりとりする場合には、CUI(キャラクタ・ユーザ・インターフェース)というわけです。

ちょっとひと息おわり】

○ホワイトボックステスト

ホワイトボックステストでは、プログラムを「中身の見える箱=ホワイトボックス」としてテストします。中身が見えるので、内部仕様に基づいた詳細なテスト(プログラムの実行経路のチェックなど)をおこなうことができます。構造をテストすることから「構造テスト」と、またはより具体的に、実行経路をテストすることから「パステスト」ともいわれます。本書では、より明示的にパステストという用語を使いたいと思います。ホワイトボックステストでは、詳細に気がとられて「プログラムがどうあるべきか」という視点を失うことがあります。

■■■■分けるか分けないか

動的テストの別の分類基準は、「分けないでテストするか」「分けてテストをするか」です。「分けない」ということは、つまり「コードを書いてしまってからテストをする」ということです。このやり方を「一斉テスト」といいます。一方「分ける」ということは、「モジュール単位ごとにテストしていく」ということです。こちらは「モジュールテスト」といいます。

○一斉テスト

初心者が一番やりがちなのは、こちらです。毎年新しくやってくる学生さんは、ほとんど最初にこれをやります。コードをわーっと書いてしまって、コンパイル。してみると、通らない。通っても挙動が変。バグを取らないといけないけれど、どこが悪いのか分からなくて、そこでスタックしていたりします。

一度は体験してみないとわからないことかもしれません。致命的でないプログラムでやってみるといいかもしれません。数千行を書き下してそういう目に遭うと、大抵嫌になれます。まれに、すごいプログラミングの天才で、書きくだしたプログラムがほとんど手をいれずにそのまま動くということがあります。筆者のような凡才プログラマでは、そんなことはとても無理なのでやりません。必ずどこかにミスを埋めこんでいたり、そもそもはじめから勘違いしたりしていることもあります。

○モジュールテスト

ある程度の規模をもったプログラムの開発に慣れてくると、意味的なまとまりごと単位に分けて、一歩一歩進めていった方が、後で確実なことに気がつくようになります。モジュールテストでは、上でちょっと説明した機能的なまとまり=モジュールを単位としてテストを行います。単位(ユニット)ごとにテストを行うので、ユニットテストとも呼ばれます。本書で扱うユニットテストとは、このことです。

モジュールテストは、複数のモジュールを組み上げてするテスト(結合テスト)をどのようにおこなうかで、さらに2つに分類されます。「最後にまとめていっぺんにやるやり方」と「ぼちぼちやってくやり方」です。

■■■■最後にまとめてかぼちぼちか

結合テストを最後にまとめていっぺんにやるやり方のことを、「ビッグバンテスト」といいます。一方、ぼちぼちやってくやり方のことを、「インクリメンタルテスト」といいます。

○ビックバンテスト

ビッグバンというのは、あれです。「芸術は爆発だ」…ではなくて、ためてためてためておいて、一挙にバーンってやつのことです。つまり、ビッグバンテストっていうのは、できあがったモジュールをいっぺんに結合してテストするやり方のことです。

次にあげるインクリメンタルテストより楽をできるように感じますが、実際にはモジュールを駆動する部品や、呼び出しているモジュールをシミュレートする部品を余計に作らないとうまくいきません。モジュールを駆動する部品のことをドライバ・モジュール、呼び出しているモジュールをシミュレートする部品のことをスタブ・モジュールといいます【図1.1.2】。個々の開発者が独立して結合テストをおこなうときには、このビッグバンテストしか選べないかもしれません。

【図1.1.2】スタブ・モジュールとドライバ・モジュール

スタブとドライバ

○インクリメンタルテスト

「インクリメンタル」というのは(ご存知とは思いますが)徐々にひとつひとつ進めていくことです。インクリメンタルテストというのは、ですから「1つのモジュールができたら、そこで周りと組み合わせてテスト、そのテストが通ったら次へ」というやり方のことです。ビックバンテストと違って、できあがった部分が存在するので、ドライバ・モジュールやスタブ・モジュールをあまり書かなくてすみます。

インクリメンタル・テストは、その実行方法の違いによって、さらに細かく分類されます。大きな分類基準としては「上からか下からか」という分け方があります。“コードコンプリート[4]”では、ほかの分類基準も挙げていますが、ここでは以下の2つを紹介します。

■■■■上から? 下から?

「上から」というのは、全体を呼び出している側から、作り始めていくやり方です。一方、「下から」というのは、末端のモジュールから、作り始めていくやり方です。

○トップダウンテスト

【図1.1.3】にトップダウンテストの概念図を示します。

【図1.1.3】トップダウンテスト

トップダウン

外界から呼び出されてすぐのモジュールから作り始めるのがトップダウンテストです。ですので、トップダウンテストでは、早期にプロトタイプを実行することができます。これは、プログラマの士気の高揚にとても役立ちます(士気+500くらい? いや、ゲームじゃないですが)。しかし、プログラムの下のほうはできてませんから、呼び出されるモジュールをシミュレートする「スタブ・モジュール」が必須になります。このスタブ・モジュールを作成するのは、結構大変です。なぜかというと、スタブがどのように機能してるかを外で観察するためのI/O機能が必要になってくるためです。このため、テストがなかなか完了しません。

○ボトムアップテスト

一方、ボトムアップテストでは、末端モジュールから作り始める【図1.1.4】ので、モジュールの仕様が単純でテスト条件が作成しやすいという長所があります。また、テスト結果をみるのも容易です。しかし、テスト対象のモジュールを呼び出す、「ドライバ・モジュール」が必要で、「最後になるまでプログラム全体が組みあがらない」という弱点もあります。

【図1.1.4】ボトムアップテスト

ボトムアップ

■■■■その他

ソフトウェア・テストにはこのほかにも、リグレッションテスト(回帰テスト、退行テストとも)、大容量テスト、負荷テスト(ストレステストとも)、有用性テスト、互換性テスト、性能テスト、異常回復テスト、機密保護テスト、構成テスト、記憶域テスト、文書テスト、導入容易性テストなどなど、沢山の種類があります。しかし、これらは本書の対象とするソフトウェア・テストではないため、説明は割愛させていただきます。気になる人は、上であげた参考書などをあたってみてください。

■■■1.1.2 この本で対象とするテスト

この本で考えようとしているテストは、上でもちょっといいましたが、モジュールテスト、ユニットテストです。末端モジュールからテストするボトムアップテストでは、ドライバ・モジュールを一般的な形で作ることができます。それをフレームワーク化したものが、xUnitと呼ばれるものになります。xUnitでは、主にモジュールのインターフェースに対するテストをおこないます。モジュールに対するブラックボックステストともいえるかもしれません。

では、インターフェースに対してのテストというのは何でしょうか? 少し、突き詰めて考えてみましょう。Bertrand Meyerさんの定義によれば、―――テストというのは、いろいろな入力を与えてみて、予測しない動作をしないかチェックすること―――だそうです。予測できる動きを動くのは当然なので、そのさらに先をチェックするのがテストというわけです。

たとえば、2つの整数をとって割り算をするような場合には、割る側の数字に0を入れてみたりすることが必要です。文字列の入力で桁数が決まっているような場合、「入力された文字列が既定桁数より多くないか?」「そもそも空列の場合に何が出力されるか?」などを調べなければいけません。また、入力というのは、引数に限らないかもしれません。場合によっては、環境設定なども入力や出力になり得ます。

オブジェクト指向プログラミング言語の中に、Eiffelという言語があります。この言語には、事前条件・事後条件という考え方があります。その関数に入る前(事前)と、出た後(事後)に成り立っているべき条件を、関数を記述するとき同時に記述するやり方です。

具体的には、次のような形で記述されます。

関数名 (引数宣言): 返り値の型 is
    require
        事前条件
    do
        関数実装
    ensure
        事後条件
    end

領域が0以上100以下座標値x, yに移動値mx, myを足しこむような移動操作ルーチンmoveを考えてみましょう。事前条件として、x, yは0以上100以下で、移動値mx, myはそれぞれx, yに加えた値が0以上100以下でなければならないとします。事後条件としては、後のx, yが前のx, yに移動値を加えたものになっていなくてはならないとしておきましょう。

とすると、移動操作moveは以下のように記述できます。

move (mx: INTEGER; my: INTEGER) is
    require
        x >= 0
        x <= 100
        y >= 0
        y <= 100
        mx >= -x
        mx <= 100 - x
        my >= -y
        my <= 100 - y
    do
        任意の実装
    ensure
        x = old x + mx
        y = old y + my
    end

このように、関数の外(入るところと出るところ)で、その関数の機能を宣言することを、「外部表明」といいます。逆に、関数の内部で各ブロックの機能などを宣言することを「内部表明」といいます。外部表明を使って、呼び出し側と呼び出される側の機能をしっかり決めつつプログラムすることを、それが契約ともみなせることから、「契約によるプログラミング(Programming by Contract)」と呼ぶことがあります。【図1.1.5】に概念図を示します。より深く知りたい方は、“プログラミング言語理論への招待[5]”を見てください。

【図1.1.5】契約によるプログラミング

契約による設計

ところが、多くの言語には、「事前条件・事後条件の記述機能」そのものがありません。また、記述できたとしても、厳密にチェックすることは、現実的には難しいです。その代わり、事前条件・事後条件をある程度網羅できる「テストの例」を作成して、動作チェックをおこなうのが現実的です。この、テストの例のことを「テストケース」と呼びます。

■■■1.1.3 テストケース

ユニットテストで考えるテストケースは、主としてインターフェースの事前条件と事後条件をチェックするためのものです。ホワイトボックステストをおこなうテストケースも書けますし、有用ではありますが、コード依存が強いため、注意する必要があります。どういうことかというと、関数の実装を変えた場合には、必ずテストケース自体を見直す必要があるのです。本書では、ブラックボックステストに重点を置いて考えていきます。

ブラックボックステストに絞っても、ひとつのインターフェースに対してテストケースがひとつということは、まずありません。XXXページで挙げた例のように、割り算ならば、少なくとも正整数、負整数の組み合わせで4ケース、0での割り算を2ケース以上は必要だと思われます。変数が多くなればなるほど、テストケースは複雑になります。

テストケースを調べ上げるには、「同値分割」「限界値分析」「原因‐結果グラフ(因果グラフ)分析」「エラー推測」といったブラックボックステストの技法を使用します。これらの技法については、“ソフトウェア・テスト技法”でそれなりに詳しく説明されていますが、「同値分割」と「限界値分析」はともかく、「原因‐結果グラフ」は、真面目にやろうとすると、かなり大変です。また「エラー推測」は名前こそついてますが、要はテストする人の山勘を使えってことだったりするので、ちょっと初心者には使えません。なお、「同値分割」は「限界値分析」に含まれるため、主に本書では限界値分析を利用して考えていきたいと思います。


1 Glenford J. Mayers著,松尾正信訳,長尾真監訳,“ソフトウェア・テストの技法”,近代科学社,1980
Glenford. J. Mayers, "The Art of Software Testing" John Wiley & Sons, Inc., 1979

2 Cem Kaner, Jack Falk, Hung Quoc Nguyen著,テスト技術者交流会訳,“基本から学ぶソフトウェアテスト”,日経BP社,2001年.
Cem Kaner, Jack Falk, Hung Quoc Nguyen, "Testing Computer Software, 2nd edition", John Wiley & Sons, Ltd., 1999.
紹介ページ

3 Shari Lawrence Pfleeger著,堀内泰輔訳“ソフトウェア工学”,ピアソンエデュケーション,2001年.
Shari Lawrence Pfleeger, ``Software Engineering: Theory and Practice, 2/E'', Prentice Hall, 2001.

4 Steve McConnell著,石川勝訳,“コードコンプリート”,アスキー出版局,1994年.
Steve McConnell, "Code Complete", Microsoft Press, 1993.

5 Bertrand Meyer著,二木厚吉監訳,酒匂寛訳,“プログラミング言語理論への招待”,アスキー出版局,1995年.
Bertrand Meyer, "Introduction to the Theory of Programming Languages", Prentice Hall, 1990.


Copyright © 2002-2003 Mika Ohtsuki. All rights reserved.