はじめに
前回は、IBMメインフレーム時代の結合テストについて見てきました。メインフレーム時代の「結合テスト」は巨大なパズルを組み立てるような作業であり、失敗すれば膨大な手戻りが発生しました。この状況を打破する鍵として生まれたのが、構造化プログラミングの手法です。
構造化プログラミングの台頭
スパゲッティコードの時代
1970年代初頭、多くのプログラムは「スパゲッティコード」と呼ばれる非構造的な形態をとっていました。GOTO文が乱用され、コードは複雑な相互参照の迷宮と化し、バグの発見や修正が極めて困難でした。エラーを1つ修正すれば、別の箇所で新たな問題が発生することもしばしば。
! GOTO文
GOTO 100 ! ラベル「100」へジャンプ
...
100 IF (X .GT. 0) GOTO 200 ! ラベル「100」で、変数Xが0より大きいかどうかを判定し、大きければ、ラベル「200」へジャンプ
Y = -1 ! Xが0以下の場合、Y = -1
GOTO 300
200 Y = 1
300 CONTINUE
あるNASAのプロジェクトで、わずか数行のコード修正が原因でロケット打ち上げ計画が数週間遅れたことがありました。この事件を契機に、開発効率を劇的に向上させる方法が求められるようになります。
構造化プログラミングの誕生
1968年、エドガー・ダイクストラが「GOTO文を排除すべきだ」と提唱する論文を発表しました。この理論に基づき、ソフトウェアを**「順次」「選択」「繰り返し」**の3つの構造に基づいて設計する手法が生まれました。これが「構造化プログラミング」です。
{ 構造化プログラミング }
if x > 0 then
y := 1
else
y := -1
end if
構造化プログラミングの特徴:
- コードの可読性向上
- プログラムの論理的構造の明確化
- デバッグと保守の容易さ
Design by Contractの誕生
契約による設計の背景
Bertrand Meyerは、1970年代後半にモジュール間の関係を「契約」として捉える考え方を提唱しました。
契約による設計とはモジュール間のインターフェースを「契約」として定義し、各モジュールが以下を保証します。
- 事前条件
モジュールが正常に動作するために必要な条件 - 事後条件
モジュールが処理後に保証する結果 - 不変条件
プロセス中に維持されるべき状態
この契約により、以下の利点が生まれました。
- モジュール間の依存関係が明確化。
- テストケース設計がシステマチックに。
- 早期に設計の不備を発見可能。
初期の結合テスト手法
トップダウンテスト vs ボトムアップテスト
1970年代、大規模システムの開発では、システム全体を小さなモジュールに分解し、段階的にテストする手法が発展しました。
また、テスト中の未完成モジュールを模倣するための「スタブ」と、テストを実行する「ドライバ」の概念が生まれました。
トップダウンテストの例:
procedure MainModule;
procedure SubModule1; // スタブモジュール
begin
result := test_data; // テスト用のダミーデータ
end;
begin
SubModule1; // サブモジュールの呼び出し
ProcessData; // メインロジックの実行
end;
ボトムアップテストの例:
procedure TestDriver; // テスト実行用のモジュール
begin
SubModule1.Input := test_data; // テスト入力の設定
SubModule1.Execute; // サブモジュールの実行
AssertEquals(expected, SubModule1.Output); // 結果の検証
end;
モジュール化時代の教訓
1. インターフェースの重要性
- インターフェースの安定性が重要
- 依存関係の明確化
- カプセル化の価値
2. テスト容易性の設計
設計原則の確立:
- 単一責任の原則
- 依存関係の最小化
- インターフェースの単純化
3. テストプロセスの標準化
- ユニットテスト
- モジュール結合テスト
- サブシステムテスト
- システム結合テスト
- 受入テスト
現代への影響
1. モジュール設計の原則
構造化プログラミング時代に確立された設計原則は、現代のソフトウェア開発の基盤となっています:
- 単一責任の原則
- 明確なインターフェース定義
- 依存関係の最小化
2. テスト戦略の進化
当時確立されたテスト手法は、現代のソフトウェア開発プロセスに直接つながっています:
# 現代のCI/CDパイプライン
stages:
- unit # ユニットテスト
- integration # モジュール結合テスト
- system # システムテスト
- acceptance # 受入テスト
3. 契約による設計の現代的展開
Design by Contractの概念は、現代のプログラミング言語や静的型付けシステムに深く組み込まれています:
// TypeScriptでの型による契約の実現
interface UserService {
// 入力の型制約 = 事前条件
getUser(id: PositiveInteger): Promise<User>;
// 戻り値の型制約 = 事後条件
updateUser(user: User): Promise<UpdateResult>;
}
4. テストの重要性の認識
この時代に確立された重要な教訓:
- コードの品質は設計段階で決まる
- テスト可能性を最初から考慮する
- モジュール間の明確な境界線の重要性