ナレッジNEW

スタブとドライバとは?作り方や活用方法を分かりやすく解説

スタブとドライバとは?作り方や活用方法を分かりやすく解説

ソフトウェア開発では、スピードと品質の両立が欠かせません。しかし実際の現場では、未完成のモジュールや処理間の強い依存関係が原因で、テストが思うように進まず、開発全体が停滞することも残念ながら少なくはありません。

実際、実装段階でモジュール(プログラム)が全てそろうのを待ってテストを行うようでは開発スケジュールが想定内で終わらなくなってしまうでしょう。

そこで以前から実装段階では「スタブ」と「ドライバ」が利用されてきました。これらは未完成の部分を補うテスト用の代替コンポーネントであり、モジュール単位のテストを独立して進められるようにするための仕組みとも言えます。

本記事では、スタブとドライバの役割の違いや活用すべき場面、さらに作成手順をJavaのテストドライバ例を用いて分かりやすく解説します。

スタブとドライバとは?

ソフトウェアテストでのスタブやドライバとは、未完成または処理単体では動作できないモジュールの代わりに一時的に使用されるコンポーネントのことを指し、ユニットテスト(Unit Testing)や統合テスト(Integration Testing)の基盤として、広く利用されています。

なお本記事では以降から、テスト対象となるプログラムのことを「テスト用コンポーネント」と表現します。

スタブとドライバの役割

スタブとドライバは、ソフトウェアテストを支援するために設計し実装するコンポーネントを指します。

実際のモジュールの動作やインターフェースを簡易的に模擬することで、呼び出し(依存)関係にあるモジュールが未完成でも、独立してテストできるようになります(図表1)。

図表1:スタブとドライバ
図表1:スタブとドライバ

スタブとは

テスト対象のモジュールと処理として接続する、まだ実装できていない、下位のモジュールを一時的に代替するシンプルで小規模なコンポーネントを指します。

「呼び出されるプログラム」として動作し、自身が呼び出された際に、テスト実行で必要となる、あらかじめ定められた戻り値を返したり、最小限の処理のみを実行したりします。これにより、未完成の依存関係にあるモジュールの実装を待つことなく、上位のモジュールがテストできるようになります。

ドライバとは

上位モジュールや呼び出しモジュールを一時的に代替し、準備ができた下位モジュールを呼び出してテスト実行するためのコンポーネントです。必要な入力データを提供し、テスト対象となる下位モジュールを呼び出して、出力結果を確認します。これにより、下位モジュールを上位モジュールと統合する前に、モジュール単体で十分にテストできるようになります。

次の図表2でスタブとドライバの違いを整理しておきます。

特徴

スタブ

ドライバ

目的

下位モジュールや未完成の依存モジュールの振る舞いを模擬し、仮のフィードバックを提供

テスト対象モジュールを呼び出したり、引数を与えたり、上位モジュールを模擬して入力を与え、実行フローを制御

振る舞い

最低限、あらかじめ定められた値を返すのみで、複雑なロジックは持たない

テスト対象モジュールに対して、実行環境や入力を提供

確認ポイント

主に、スタブとの相互作用後にテスト対象モジュールの状態を確認

呼び出された下位モジュールの状態や振る舞いを確認

テスト戦略

主にトップダウン(Top-Down)としてユニットテストで使用

主にボトムアップテスト(Bottom-Up)として統合テストで使用

役割

呼び出されるプログラム(Called program)

呼び出しプログラム(Calling program)

構造

シンプル

スタブより複雑

図表2:スタブとドライバの違い

スタブとドライバを使うことのメリット

スタブとドライバの代表的なメリットは以下の通りです。

スタブとドライバ共有のメリット

開発スピードの向上

スタブやドライバを利用することで、開発者は異なるモジュールを並行して開発できます。全てのコンポーネントが完成するのを待つのではなく、スタブやドライバで各モジュールを独立してテストできるため、全体の開発速度が向上します。

バグの効率的な切り分けが可能

テスト対象モジュールを外部依存から隔離することで、スタブやドライバは問題発生時の原因範囲を絞り込むことができます。これによって、他のモジュールに起因するバグの影響を受けずに、開発者が根本的な原因を特定しやすくなります。

コストとリソースの削減

スタブとドライバは複雑なシステムや外部リソース(実際のデータベースや決済サービスなど)にアクセスせずにテストできるため、コストやリソースの節約が可能になります。

スタブのメリット

境界値や例外ケースのコントロールが可能

スタブは、境界値、システム障害や予期しないケース(ネットワーク切断やAPIエラーなど)を模擬できるように構成可能です。これにより、テスト対象のモジュールが異常なシナリオやエラーパターンに対してどのように反応するかをテストできます。

安定性と再現性の確保

スタブは予測可能なレスポンスを返すことで、テストの安定性と毎回同じ結果が得られることを保証します。これはテスト自動化システムにとって非常に重要なポイントです。

統合テストの手法とスタブ・ドライバの役割

スタブやドライバを統合テストで活用するには、トップダウン、ボトムアップ、サンドイッチといった統合テストの基本的な手法を理解する必要があります。

トップダウンテスト(上位モジュールから)

定義

トップダウンテストは、最上位のモジュール(例えば、ユーザーインターフェースやメインモジュール)からテストを開始し、順に下位のモジュールをテストしていく手法です。この手法は上から下への制御フローに従い、重要な処理を優先してテストしていきます(図表3)。

図表3:トップダウンテスト
図表3:トップダウンテスト

テスト方法

  1. 最上位のモジュールを選択します。(例:メイン画面のモジュールA)
  2. 下位モジュール(モジュールB/Dなど)をスタブで模擬します。
  3. モジュールAとスタブBでテストを行い、処理フローを確認します。
  4. 実際のモジュールが完成したら、スタブBに置き換え、再テストを実施します。
  5. モジュールが統合された段階で終了とします。

ボトムアップテスト(下位モジュールから)

定義

ボトムアップテストは、最も下位のモジュール(データ処理関数などの基本的なコンポーネント)からテストを開始し、順に上位モジュールへ統合していく手法です(図表4)。この方法は、まず基盤となる処理のテストに重点を置きます。

図表4:ボトムアップテスト
図表4:ボトムアップテスト

テスト方法

  1. 最下位のモジュール(例:データ処理のモジュールC)を選択します。
  2. 上位モジュール(A/Bなど)をドライバでシミュレーションします。
  3. モジュールCとドライブBのテストケースを実行し、内部ロジックと出力を検証します。
  4. ドライブBを実際のモジュールBに置き換え、モジュールB+CとドライブAで再テストします。

この手順を繰り返し、モジュール全体が統合されるまで続けます。

サンドイッチテスト(ハイブリッド)

定義

サンドイッチテストは、トップダウンテストとボトムアップテストを組み合わせた手法です。上位層と下位層の両側から同時にテストを進め、中間層(ターゲットレイヤー)で統合します(図表5)。これにより、両手法の利点と欠点をバランス良く補います。

図表5:サンドイッチテスト
図表5:サンドイッチテスト

テスト方法

  1. 中間層(例:メインビジネス層のモジュールB)を特定します。
  2. 下位モジュール(C)はスタブで、上位モジュール(A)はドライバでシミュレートします。
  3. Bを単体でテストします。
  4. モジュールA+B(ドライブAを実際のモジュールAに置き換える)と、B+C(スタブCを実際のモジュールCに置き換える)を統合します。
  5. A-B-C間の相互作用を検証します。
  6. 最終的にモジュール全体に拡張します。

スタブ・ドライバの作り方

本項では、スタブおよびドライバを用いた「学生管理アプリケーション(Student Management Application)」の開発例からスタブ・ドライバの作り方を説明します。

このアプリケーションは以下の3モジュールで構成します。

  • Module A – StudentService: 学生情報管理モジュール(未完成)
  • Module B – CourseService: コース管理モジュール(完成済み)
  • ModuleC – EnrollmentService: 学生のコース登録処理モジュール(開発中)

モジュール間の統合テストを行う必要がありますが、次のような課題があるとします。

  • 一部のモジュールが未完成
  • 未完成のモジュールを依存関係にある

このため、以下のようにスタブとドライバを用いることにします。

  • スタブ:呼び出される側(callee)のモジュールを代用し、応答を模擬
  • ドライバ:呼び出す側(caller)のモジュールを代用し、テスト対象モジュールへの呼び出しを模擬

Javaでのスタブの作り方

Javaでのスタブの作り方をステップに分けて説明します。

シナリオ

EnrollmentService モジュールが StudentService を呼び出して学生情報を取得します。この段階では、StudentService はまだ完成していないため、代わりに スタブを作成します(図表6)。

図表6:StudentServiceStubの構成
図表6:StudentServiceStubの構成

ステップ1:StudentService のインターフェースを作成

// Interface for StudentService 
public interface StudentService { 
    String getStudentName(String studentId); 
} 

ステップ2:スタブを実装

// Stub to simulate StudentService 
public class StudentServiceStub implements StudentService { 
    @Override 
    public String getStudentName(String studentId) { 
        if (studentId.equals("S001")) return "Yamada Taro(山田 太郎)"; 
        if (studentId.equals("S002")) return "Suzuki Hanako(鈴木 花子)"; 
        if (studentId.equals("S003")) return "Tanaka Ichiro(田中 一郎)"; 
        return "Unknown Student"; 
    } 
} 

ステップ3:メインモジュール(EnrollmentService)でスタブを使用

public class EnrollmentService { 
    private final StudentService studentService; 

    public EnrollmentService(StudentService studentService) { 
        this.studentService = studentService; 
    } 

    public String enrollStudent(String studentId, String courseId) { 
        String name = studentService.getStudentName(studentId); 
        if ("Unknown Student".equals(name)) { 
            return "登録失敗: 学生が見つかりません (" + studentId + ")"; 
        } 
        return "登録完了: " + name + " がコース " + courseId + " に登録されました。"; 
    } 
} 

ステップ4:スタブを用いた統合テストを実施

public class EnrollmentTest { 
    public static void main(String[] args) { 
        StudentService stub = new StudentServiceStub(); 
        EnrollmentService enrollment = new EnrollmentService(stub); 

        System.out.println(enrollment.enrollStudent("S001", "C101")); 
        System.out.println(enrollment.enrollStudent("S999", "C101")); 
    } 
} 

期待結果

登録完了: Yamada Taro(山田 太郎) がコース C101 に登録されました。 
登録失敗: 学生が見つかりません (S999)

Javaでのドライバの作り方

Javaでのドライバの作り方をステップに分けて説明します(図表7)。

シナリオ

図表7:StudentServiceDriver の構成
図表7:StudentServiceDriver の構成

StudentService モジュールはすでに完成していますが、呼び出し元(caller)であるEnrollmentServiceはありません。そのため、StudentService を単独でテストするために ドライバ を作成します。

ステップ1:テスト対象モジュール(StudentServiceImpl)を実装

public class StudentServiceImpl implements StudentService { 
    @Override 
    public String getStudentName(String studentId) { 
        switch (studentId) { 
            case "S001": return "Yamada Taro(山田 太郎)"; 
            case "S002": return "Suzuki Hanako(鈴木 花子)"; 
            case "S003": return "Tanaka Ichiro(田中 一郎)"; 
            default: return "Unknown"; 
        } 
    } 
} 

ステップ2:呼び出し元モジュールを模擬するドライバを作成

public class StudentServiceDriver { 
    public static void main(String[] args) { 
        StudentService service = new StudentServiceImpl(); 

        testStudent(service, "S001"); 
        testStudent(service, "S999"); 
    } 

    private static void testStudent(StudentService service, String studentId) { 
        String name = service.getStudentName(studentId); 
        if (!"Unknown".equals(name)) { 
            System.out.println("学生ID: " + studentId + " → " + name); 
        } else { 
            System.out.println("学生ID: " + studentId + " は存在しません。"); 
        } 
    } 
} 

期待結果

学生ID: S001 → Yamada Taro(山田 太郎) 
学生ID: S999 は存在しません。 

スタブ・ドライバの実装に関するガイドと実践的なポイント

スタブ・ドライバを効果的に活用するためには、その動作原理と、ベストプラクティスを理解することが重要です。

テスト目的を明確にする

スタブやドライバを作成する前に、どのモジュールのどの処理をテストしたいのか、またどの依存関係を分離する必要があるのかを明確にしておきます。

スタブをシンプルで集中したものに保つ

スタブはテストに必要最小限の動作を模擬すべきです。 関係のない複雑なロジックを加えると、スタブ自体に不具合が入り込みやすくなるからです。

インターフェースを活用する

可能な限り、依存関係にあるものはインターフェースとして定義しましょう。こうすることで、特定のクラスを継承する必要がなく、そのインターフェースを簡単に実装したスタブクラスを安易に作成できるようになります。

できるだけスタブを再利用する

動作が固定的、または頻繁に使われる依存関係があるモジュールには、再利用可能なスタブ を作成しておくとコードの重複が減らせます。

モッキング(Mocking)フレームワークの活用を検討する

Javaでは、Mockito などのフレームワークを利用することで、スタブやモックオブジェクトの作成が自動化できるためボイラープレートコードの削減や効率向上が期待できます。特に、複雑な動作の設定やインタラクションの検証が容易になります。

スタブで実モジュールの挙動を適度に再現する

スタブはシンプルであるべきですが、過不足なくテストできるよう、実モジュールの基本的な動作を十分に模擬する必要があります。スタブが本実装と大きく異なってしまうと、テストがパスしても実際のシステムではエラーが発生する可能性があるからです。

スタブ・ドライバ自体をテストする

スタブやドライバにも不備が含まれる場合があります。本実装のコードほど厳密なテストは不要ですが、意図した通りの動作をしているか程度にはテスト実行し確認すべきです。

不要になったスタブ・ドライバを削除する

スタブやドライバは一時的なコンポーネントです。実際のモジュールが開発・統合された後は、スタブ・ドライバを本来のモジュールに置き換え、回帰テストなど特別な用途がない限り、削除するようにします。

スタブ・ドライバを用いた一般的なテストの流れ

スタブ・ドライバを用いたテストの一般的な流れは以下の通りです。

1. テスト対象モジュールを特定する(Module Under Test:MUT)

テストしたい特定のモジュールを選定します。

2. MUTの依存関係を分析する

MUTが依存または連携している全てのモジュールやサービスを特定します。

3. 統合方式を決定する

上位から下位へ(トップダウン:下位モジュール用のスタブが必要 )または下位から上位へ(ボトムアップ:上位モジュール用のドライバが必要 )統合テストを行うかを決定します。

4. 下位モジュール用のスタブを作成する(トップダウンの場合)

MUTが呼び出す下位モジュールの動作を模擬するためのスタブを、フレームワークやコードを用いて作成します。作成したスタブはMUTに想定される戻り値や模擬動作を提供します。

5. MUT用のドライバを作成する(ボトムアップの場合)

MUTを呼び出し、入力を与えるドライバを作成します。これは本来MUTを呼び出す上位モジュールを模擬するものです。

6. テストケースを作成する

MUTに対して正常系、異常系、境界値などのケースを含む具体的なテストシナリオを作成します。

7. テストを実行する

スタブまたはドライバを使用してMUTを独立させた状態でテストケースを実行します。

8. 結果を分析する

実際の結果と期待結果を比較します。

  • エラーがある場合、MUTのロジックを確認します。
  • スタブ・ドライバの設定が誤っている場合、結果が正しくない可能性があります。

9. 繰り返しと改善を行う

このプロセスを他のモジュールにも繰り返し適用します。モジュールが完成し、段階的に統合されるにつれて、スタブ・ドライバを実際のモジュールに置き換えていきます。

まとめ

本記事では、スタブ・ドライバの定義や相違点などを解説し、さらにそれらがトップダウン方式およびボトムアップ方式といった統合テスト手法において果たす役割について説明しました。実際には、テスト対象のモジュールを実装する開発環境やプログラミング言語に沿ったスタブ・ドライバの実装方法や利用方法を正しく理解しておく必要があります。

ここまで紹介してきたように、実際の開発においてモジュール間の統合テストをスムーズに進めるには、設計(どこを仮置きするか)と実装(どう作るか)のバランスが重要となります。

■参考資料■

SNSシェア

この記事は面白かったですか?

今後の改善の参考にさせていただきます!

Search Articles By The Cast出演者/執筆者から記事を探す

Search Articless By The Categoryカテゴリから記事を探す

Ranking

ランキング

もっと見る