前史 — デバッグからテストへ、体系化前夜の30年

1950年代、 コンピュータ黎明期のプログラマにとって「デバッグ」と「テスト」は同じものだった。 1968年のNATOソフトウェア工学会議が突きつけた「ソフトウェア危機」、 Dijkstraの「テストはバグの不在を証明できない」という警句、 そして1979年 Glenford Myers『The Art of Software Testing』が果たした最初の体系化まで。 「テスト」が独立した知的領域になるまでの30年を辿る。

testinghistorydebuggingwaterfalldijkstrasoftware-engineering

テストという言葉が、 まだ意味を持たなかった時代

今日「ソフトウェアテスト」と呼ばれる活動は、 最初から独立した工程として存在していたわけではない。 1950年代のコンピュータ黎明期には、 「プログラムを動かしてみて、 動かなければ直す」というデバッグ(debugging)と、 「プログラムが正しく動くことを確認する」というテスト(testing)は、 ほとんど区別されていなかった。

本記事では、 この未分化の時代から、 1979年の Glenford Myers 著『The Art of Software Testing』によってテストが一つの知的領域として体系化されるまでの、 約30年間の歩みを追う。 シリーズ overview で示した中心の問い — 「プログラムが正しいことをどう確かめるか」 — が、 まだ誰にも明確な輪郭を与えられていなかった時代の物語である。

道のりは、 大きく三つの節目を通る。 まず1960年代、 ソフトウェアの規模拡大が「ソフトウェア危機」として社会問題化する。 次に1970年前後、 ウォーターフォールという工程観のなかにテストが位置づけられ、 同時に Dijkstra がその工程観の限界を鋭く突く。 そして1979年、 Myers の著書がテストを初めて一冊の理論として体系化する。 この三段跳びを、 一つずつ追っていこう。

1947年、 蛾が挟まった話 — 「バグ」という言葉の由来

本格的な前史に入る前に、 一つ有名な逸話に触れておきたい。 1947年、 ハーバード大学の Mark II リレー式計算機が動作不良を起こし、 調べてみるとリレーの接点に実際の蛾(moth)が挟まっていたという出来事があった。 Grace Hopper が率いたチームは、 この蛾をログブックに貼り付け、 「バグの実例第一号(first actual case of bug being found)」と記録したと伝えられている。

この逸話が「バグ(bug)」という語をソフトウェア分野に広めた契機になったとされることが多い。 ただし正確には、 “bug” という語自体はこれよりずっと以前、 19世紀のエジソンの書簡にも技術的な不具合を指す言葉として登場しており、 Hopper のチームが言葉そのものを発明したわけではない。 とはいえ、 この蛾の逸話が「不具合には具体的な原因があり、 見つけて取り除くことができる」という発想を鮮やかに象徴していることは間違いなく、 テスト史の最初のページを飾るにふさわしい一幕である。

1950年代 — デバッグとテストが区別されなかった時代

初期のコンピュータ(ENIAC、 EDSAC など)向けのプログラミングでは、 プログラムを書いた本人が機械の前で動作を確認し、 うまく動かなければその場で修正するというサイクルがすべてだった。

当時は「テスター」という職能も、 「テスト」という独立した概念も存在せず、 正しく動くプログラムを作ること自体がゴールであり、 そのための試行錯誤はまとめて「デバッグ」と呼ばれていた。 テスト計画書やテストケースの設計といった、 今日では当たり前の成果物もまだ一般的ではなかった。 プログラムの正しさは、 開発者本人の頭の中にある「仕様の理解」と実際の出力を突き合わせることでしか確認できなかった。

この時代の状況を象徴するのが、 1957年に Charles L. Baker が著した書評の中で示したとされる指摘である。 彼は「プログラムのデバッグ」と「プログラムのテスト」は別の活動だと初めて明確に区別した人物の一人と言われる。 これ以降、 1970年代末にかけて、 テストを独立した工程として捉える見方が徐々に広がっていった。

1960年代 — ソフトウェア危機とテスト研究の萌芽

1960年代に入ると、 ソフトウェアの規模と複雑さが急速に増大し、 開発の遅延・予算超過・品質低下が社会問題化した。 ハードウェアの性能向上に伴ってプログラムの野心も膨らみ、 個人や小さなチームの経験則だけでは制御しきれない規模のシステムが次々と生まれていった。

この状況は1968年、 ドイツ・ガルミッシュで開催された NATO ソフトウェア工学会議(NATO Software Engineering Conference)で「ソフトウェア危機(software crisis)」という言葉で表現され、 ソフトウェア工学(software engineering)という分野が公式に名付けられる契機となった。 この会議の議事録は Peter Naur と Brian Randell の編集により報告書としてまとめられ、 後の業界に大きな影響を与えている。 品質やテストの不足が、 規模拡大に伴う失敗の一因として繰り返し指摘された点は、 テスト史の観点からも重要である。

この危機への反応は大きく二つの方向に分かれた。 一つは、 Robert Floyd や C.A.R. Hoare らが進めた形式的検証(formal verification)、 すなわちプログラムが数学的に正しいことを証明しようとするアプローチである。 もう一つは、 実際にプログラムを動かして誤りを見つけ出す、 経験的なテストのアプローチである。 後者の系譜における初期の重要な仕事として、 J. C. Miller と C. J. Maloney が1963年に発表した論文「Systematic Mistake Analysis of Digital Computer Programs」がある。 グラフ理論を用いてプログラムの構造を分析し、 体系的にテストケースを設計しようとするこの研究は、 後のパステスト(path testing)やカバレッジ(coverage)概念の先駆けと位置づけられている。

巨大プロジェクトの苦闘 — OS/360とThe Mythical Man-Month

ソフトウェア危機を象徴する事例としてしばしば引用されるのが、 IBM のメインフレーム用 OS「OS/360」の開発である。 プロジェクトマネージャーを務めた Fred Brooks は、 その経験を1975年の著書『The Mythical Man-Month(人月の神話)』にまとめた。

この本自体はマネジメント論を主題としているが、 大規模システムにおいてテストと統合作業がいかに見積もりを超えて肥大化するか、 そしてスケジュールの遅延に対して人員を追加投入することがなぜ逆効果になりやすいか(いわゆる「ブルックスの法則」)を具体的に描いている。 テストと統合フェーズの困難さが広く共有されたことも、 後にテストを独立した工程・専門領域として扱う必要性を業界に印象づける一因になった。

ウォーターフォールとVモデル — テストは「工程の最後」に位置づけられた

1970年、 Winston Royce が論文「Managing the Development of Large Software Systems」の中で、 要件定義 → 設計 → 実装 → テスト → 運用という段階を順に進める開発モデルを示した。 これが後に「ウォーターフォールモデル(waterfall model)」と呼ばれるようになる図式である。

皮肉なことに、 Royce 自身はこの論文の中で反復とフィードバックの必要性を強調していたのだが、 後世にはテストが工程の末尾に置かれた一直線のプロセスとして単純化されて広まった。 この構造のもとでは、 テストは「作り終えたものが正しいかを最後にまとめて確認する検収作業」として扱われがちだった。

各開発フェーズに対応するテストフェーズ(単体テスト、 結合テスト、 システムテスト、 受け入れテスト)を対にして図示する「V字モデル(V-model)」という整理は、 ウォーターフォール的な工程観をより明示的に描いたものである。 ただし、 これが体系だった標準として広まるのは実はこの前史よりも後、 1980年代半ば以降のことである。 西ドイツ国防省が主導した V-Modell は1986年に開発が始まり、 1992年に国防分野での使用が義務化され、 1997年には連邦政府標準として“V-Modell 97”が公式化されている。

とはいえ、 「開発の各段階に対応するテストの階層がある」という発想の土台は、 本記事で扱う1970年前後のウォーターフォール的な工程観にすでに存在していた。 テストが工程の最後に配置されるという構造は、 後年「テストは手戻りコストが大きい段階でしか実施されない」という批判を招くことになる。 この問題意識は、 のちの第11話で扱う「早期に、 頻繁に」というテストの現代的な原則へとつながっていく。

Dijkstraの批判 — 「テストはバグの存在を示せるが、 不在は示せない」

ウォーターフォール的な「最後にまとめてテストする」工程観に対して、 コンピュータ科学者 Edsger W. Dijkstra は根本的な疑義を呈した。 彼の言葉として広く知られているのが、 次の一節である。

“Program testing can be used to show the presence of bugs, but never to show their absence!” (プログラムテストはバグの存在を示すためには使えるが、 その不在を示すためには決して使えない)

この主張は、 1969年の NATO ソフトウェア工学会議の議事録に見られる初期の変奏を経て、 1970年前後に執筆された未発表ノート「Notes on Structured Programming」(EWD249)の中で明確な形を得たとされる。

Dijkstra の含意は、 「テストによって網羅的な正しさの証明はできない。 有限個のテストケースをいくら積み重ねても、 テストしていない入力に対する誤りの不在は保証できない」という点にある。 彼自身はこの限界を根拠に、 テストよりも構造化プログラミング(structured programming)や形式的な正しさの議論を重視すべきだと主張した。 この批判は今日でもソフトウェアテストの本質的な限界を語る際に必ず引用される、 テスト史上もっとも有名な一節である。

同時に、 この限界を認めた上でなお「テストは有用である」という立場を体系立てて示したのが、 次に述べる Glenford Myers の仕事だった。 Dijkstra が理論的な限界を突きつけたのに対し、 Myers は「限界があってもなお、 テストは体系的に行えば十分に価値がある」という実践的な立場から答えを返したとも言える。

Glenford Myers『The Art of Software Testing』(1979) — テストの初めての体系化

1979年、 IBM でソフトウェア工学に携わっていた Glenford J. Myers は、 著書『The Art of Software Testing』(John Wiley & Sons)を出版した。 これはソフトウェアテストだけを主題とした、 最初期の体系的な書籍の一つとされる。

Myers がこの本で示した最も有名な定義は次のものである。

テストとは、 誤りを見つける意図(intent)を持ってプログラムを実行するプロセスである。

これは当時広く信じられていた「テストとはプログラムが仕様どおりに正しく動くことを示すための作業である」という理解を転換させるものだった。 Myers に言わせれば、 「正しく動くことを確認する」という心構えで臨むテストは無意識のうちに甘くなる。 むしろ「このプログラムのどこかに必ずバグがある、 それを見つけてやろう」という破壊的な意図を持ってテストを設計・実行するときにこそ、 テストは効果を発揮する。

この考え方から、 「成功したテストケースとは、 バグを見つけたテストケースである」という逆説的な評価基準が導かれた。 この評価基準は、 テストの成果を「バグが出なかったので安心」ではなく「バグを見つけられたかどうか」で測るという発想の転換であり、 今日のテスト設計思想の出発点の一つになっている。

Myers はさらに「テストの心理学(psychology of testing)」という章を設け、 プログラムの作者自身がそのプログラムをテストすることの危うさを指摘した。 作者は自分が書いたコードの盲点に気づきにくいため、 独立した第三者によるテストやレビュー、 ウォークスルー(walkthrough)、 インスペクション(inspection)が有効だと論じている。 加えて、 境界値分析(boundary value analysis)や同値分割(equivalence partitioning)といった、 限られた時間で効果的なテストケースを設計するための技法も紹介している。 これらは、 無限に近い入力の組み合わせを、 限られた時間の中でどう代表的なケースに絞り込むかという実践的な問いに答えるための技法であり、 今日に至るまでテスト技法の基本語彙であり続けている。

検証(Verification)と妥当性確認(Validation)

同じ1979年、 Barry Boehm は論文の中で、 次の対比を定式化したとされる。

概念 問い 意味
検証(Verification) Are we building the product right? 仕様通りに正しく作れているか
妥当性確認(Validation) Are we building the right product? そもそも作るべきものを作っているか

単体テスト・統合テストの大半は「検証」の領域(仕様に対する適合を確認する)にあたる。 一方、 ユーザーインタビューや受け入れテストは「妥当性確認」の領域に近い。 「テストを全部通した」ことは検証の達成であって、 妥当性確認の達成を意味しない — 正しく作られた「間違ったもの」は、 テストが緑でも失敗作になり得る。 この区別は、 シリーズ overview で触れた4つの糸のうち最初のものであり、 以後の全ての話で繰り返し立ち返ることになる。

欠陥修正コストの逓増説 — テストへの投資はなぜ正当化されたか

「バグは発見が遅れるほど修正コストが跳ね上がる」という主張は、 テストへの投資を正当化する根拠として長く引用されてきた。 Boehm は1976年の論文、 および1981年の著書『Software Engineering Economics』で、 要件定義段階で見つかる欠陥に比べ、 本番運用後に見つかる欠陥の修正コストは数十倍から百倍に達すると図示した — いわゆる「欠陥修正コストのカーブ」である。 この主張は、 テストを工程の早い段階に前倒しすべきだという発想の理論的な支柱として、 長く業界に浸透した。

ただし、 この「カーブ」の実証的な裏付けは近年疑問視されている。 Laurent Bossavit は著書『The Leprechauns of Software Engineering』(2015)で、 Boehm の主張の根拠となった調査を精査し、 「学生を対象にした小規模な調査結果を一般化している」「引用が孫引きで元データを辿れない」といった問題を指摘し、 「経験的に裏付けられた事実というより、 Boehm の意見が図示されたものに過ぎない」と結論づけた。 これは「早期に見つけたほうが直しやすい」という直感そのものを否定するものではないが、 「本番の欠陥は要件段階の百倍のコストがかかる」といった具体的な数字を鵜呑みにすべきではない、 という健全な懐疑を業界にもたらした。 テスト史を辿るうえでは、 定説として広まった主張であっても、 後年になって検証し直されることがあるという点も覚えておきたい。

初期のテスト自動化の萌芽

この時代のテストは、 基本的に手作業だった。 メインフレーム上のバッチ処理では、 テストデータを収めたカードやテープを用意し、 プログラムを実行して出力を目視や手作業で期待値と突き合わせる、 という方法が一般的だった。 COBOL や FORTRAN のシステム開発現場では、 テストケースを紙の文書として管理し、 担当者がチェックリストに沿って手動で確認する体制が主流だった。 テストの実行結果は紙の帳票として出力され、 人間の目でひとつひとつ照合するのが当たり前の光景だった。 同じテストを再実行するだけでも多くの手間がかかり、 「テストを何度も繰り返す」というコスト意識自体が今日とは大きく異なっていた。

とはいえ、 自動化の芽がまったくなかったわけではない。 Miller and Maloney(1963)のような、 プログラムの構造をグラフとして分析し体系的にテストケースを導出しようとする研究は、 後年のカバレッジ計測ツールやテストケース生成ツールの先駆けと見なせる。

また大規模なメインフレームベンダーの内部では、 回帰確認のために過去の出力と新しい出力を機械的に比較する簡易な仕組みが使われ始めていた。 しかし、 今日の xUnit のような「誰もが使える共通の単体テストフレームワーク」はまだ存在せず、 テストコードを書くこと自体が標準的な開発作業として認識されるには至っていなかった。

体系化前夜 — 1970年代末の到達点

1970年代末までに、 次のような土台が出来上がっていた。

観点 1950年代の状況 1979年時点の状況
テストの目的観 デバッグと未分化、 動けばよい 誤りを見つける意図を持つ独立した活動
実施者 プログラマ本人が場当たり的に確認 作者以外による検証の有効性が論じられる
技法 特になし、 その場しのぎの確認 境界値分析・同値分割などの技法が整理される
位置づけ 開発と一体化した作業 開発工程の中に明示的に位置づけられた工程
限界の自覚 ほぼ意識されていない Dijkstra の批判により原理的限界が広く共有される

この時点でテストは、 ようやく「独自の理論と技法を持つ専門領域」として輪郭を持ち始めた。 しかし、 テストを日常的に、 自動的に、 繰り返し実行するための道具立てはまだなかった。

この30年間を振り返ると、 テストという営みは「概念すら存在しない状態」から「理論と技法を備えた専門領域」へと、 着実に歩みを進めたことがわかる。 だが、 その歩みはあくまで 理論の整備 にとどまっていた。 Myers の本を読んで境界値分析を学んだプログラマも、 テストケースを書く作業自体は依然として手作業で、 一度書いたテストを何度も同じ手間をかけて再実行し続けなければならなかった。 「良いテストとは何か」という問いには答えが出つつあったが、 「そのテストを日常的に、 摩擦なく実行し続けるにはどうすればよいか」という問いには、 まだ誰も答えていなかった。

この空白を埋めたのが、 1990年代半ばに Kent Beck が Smalltalk 向けに作った小さなフレームワーク SUnit であり、 そこから生まれた JUnit だった。 次の第2話では、 この転換点 — 伝説の飛行機の中で書かれたという JUnit の逸話とともに — を詳しく見ていく。

← Back to ソフトウェアテストの系譜