0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【結合テスト】“全部”は無理。最小で抜け漏れないテストケースを設計する思考法と手順

0
Posted at

はじめに

「テストケース、これで全部だっけ…?」
「“思いつく限り”でテストしたけど、本当に大丈夫だろうか…?」
開発者なら誰しも、そんな不安に駆られたことがあるのではないでしょうか。

特に、複数のモジュールを組み合わせる 結合テスト(Integration Test, IT) では、入力・状態・タイミングの組み合わせが爆発しやすく、「全数テスト」は現実的ではありません。

この記事では、「全数テストは不可能」という前提に立ち、最小の労力で、抜け漏れなく、効果的な結合テストケースを設計するための考え方と具体的な手順を、テンプレート付きで解説します。

※本記事の「IT」は Information Technology ではなく Integration Test(結合テスト)を指します。

0. 前提整理:UT / IT / ST の役割を混ぜない

まず、「どのテストで何を保証するのか」を明確にするために、テスト工程の役割を揃えましょう。ここがブレると、テストの目的が曖昧になり、抜け漏れの原因になります。

※現場によっては単体テストを「PT」と呼ぶこともありますが、読み手によって解釈が分かれる可能性があるため、本記事では UT / IT / ST に統一します。

  • UT (Unit Test / 単体テスト):

    • 対象: メソッド、関数、コンポーネント、画面など、単体で動作する最小単位。
    • 目的: 「部品」が仕様通りに正しく動作することを確認する。ロジックの網羅性を重視。
  • IT (Integration Test / 結合テスト):

    • 対象: 複数のモジュールを組み合わせた、ひとまとまりの機能。
    • 目的: モジュール間の 接続点(インターフェース) に不整合がないか、データが正しく受け渡されるか、連携時の副作用がないかを確認する。
  • ST (System Test / 総合テスト・システムテスト):

    • 対象: システム全体。
    • 目的: 本番に近い環境で、システムが 要求仕様 をすべて満たしているかを確認する。機能だけでなく、性能、セキュリティ、ユーザビリティなども含めて検証する。

この記事は、IT(結合テスト) にフォーカスします。単体では動いても、繋いだ途端に動かなくなる不具合を効率的に見つけ出すのがゴールです。

1. 「最小で抜け漏れなく」を成立させる3つの大前提

完璧なテストは存在しません。だからこそ、私たちは賢くテストを設計する必要があります。以下はそのための3つの重要な考え方です。

(1) テストは「欠陥がない」ことを証明できない

テストで見つけられるのは、欠陥があることだけだ。
(Testing shows the presence, not the absence of bugs.)
-- エドガー・ダイクストラ

テストで言えるのは、「この条件では欠陥が見つからなかった」ということまでです。欠陥が存在しないことの証明(いわゆる“悪魔の証明”)はできません。

だからこそ、私たちは 「抜け漏れ=重大なリスクを見落とすこと」 と捉え、リスクの高い箇所を狙い撃ちする戦略が不可欠になります。

(2) 全数テストは不可能

ごく単純なソフトウェアを除き、すべての入力と状態の組み合わせを試すことは時間的にもコスト的にも不可能です。
そこで、テスト技法リスクベースのアプローチ を用いて、テストすべき点に労力を集中させます。目指すのは「少ないケースで最大の情報を得る」効率的な設計です。

(3) 欠陥は偏在する(パレートの法則)

バグの80%は、コード全体の20%の部分に集中している。

欠陥は、なぜか特定のコンポーネントに集中する傾向があります。特に、「複雑なロジック」「難易度の高い技術」「開発者の経験が浅い箇所」「過去に不具合が多発した箇所」 は重大なバグが潜んでいる可能性が高い“ホットスポット”です。

したがって、すべての機能を“同じ粒度で均等に”テストするのではなく、リスクに応じて濃淡をつけるのが最も合理的です。

2. 結合テストの「観点」を先に固定する(抜け漏れ防止の核)

テストケースを「思いつき」で書くと、必ず漏れが出ます。
そこで、最初に「何を確認すべきか」という 観点 をテンプレートとして固定します。結合テストで最低限押さえるべき観点は、この4つに集約できます。

  1. インターフェイス確認

    • 何を? モジュール間のデータの受け渡し。
    • 具体例:
      • リクエスト/レスポンスのデータ形式、型、桁数、順序は正しいか?
      • 必須パラメータの欠落、余分なパラメータの付与を正しく処理できるか?
      • 正常時/異常時のHTTPステータスコードやエラーコードは仕様通りか?
  2. 期待結果の確認

    • 何を? 一連の処理を実行した結果が、仕様またはユーザーの期待と一致しているか。
    • 具体例:
      • 画面の表示項目、計算結果、メッセージは正しいか?
      • データベースのレコードは正しく作成/更新/削除されているか?
      • 外部システムへの通知は正しく行われているか?
  3. データの確認

    • 何を? 様々なパターンの入力データに対して、システムが正しく振る舞うか。
    • 具体例:
      • 正常値: 想定される一般的なデータ。
      • 異常値: 想定外のデータ(例: 文字列を入れるべき所に数字、不正な形式のメールアドレス)。
      • 境界値: 仕様の境界となる値(例: 0, 最大値, 最大値+1)。
  4. エラー発生可能性の確認

    • 何を? 不具合が起きやすい状況(エッジケース、境界条件、レース条件など)を意図的に作り出し、システムの堅牢性を確認する。
    • 具体例:
      • タイミング: 二重クリック、決済中のタイムアウト、非同期処理の前後関係。
      • リソース: 在庫切れ、権限不足、DBの排他制御。
      • 復旧: 処理失敗後のリトライ、データのロールバック。

この 「観点4点セット」 をチェックリストとして使うことで、個人のスキルや経験に依存せず、テストの抜け漏れを劇的に減らすことができます。

3. 手順:最小で抜け漏れないテストケース設計(7ステップ)

それでは、具体的な設計手順を見ていきましょう。

Step1:結合の“境界”を決める(何と何の結合を見る?)

最初に、テスト対象の全体像を把握し、「どこ」と「どこ」の結合をテストするのかを一覧化します。

  • 対象コンポーネント: 画面、API、バッチ、データベース、外部インターフェイス(決済システム、メールサーバー等)
  • 接続点: API仕様、イベント、メッセージキュー、トランザクション境界
  • 責任範囲: どこまでをITで担保し、どこからをSTに任せるか

成果物: 結合点リスト(Interface Map)を作成すると、関係者との認識合わせもスムーズです。

No. 接続元 接続先 インターフェイス 備考
1 UI 注文API REST 商品購入リクエスト
2 注文API 認証API REST ユーザー認証
3 注文API 在庫DB SQL 在庫引き当て
4 注文API 決済GW REST 外部決済連携

Step2:シナリオ(業務フロー)で“通すべき道”を先に作る

個々の機能だけでなく、「一連の流れ」でシステムが正しく動作するかを確認するために、まず主要なシナリオ(ユースケース)を洗い出します。

コツ:シナリオはまず最小構成で良い

  • Happy Path(正常系): 最も重要で、最も使われるであろう基本的な操作の流れ。
  • 代表的な異常系: バリデーションエラー、権限エラー、外部連携失敗など、起こりうる代表的なエラーケース。
  • 復旧系: リトライ、二重送信、タイムアウトなど。結合テストで不具合が顕在化しやすい最重要ポイント。

Step3:各シナリオを「観点4点セット」に分解する

Step2で作った各シナリオに対して、セクション2で定義した「観点4点セット(インターフェイス / 期待結果 / データ / エラー発生可能性)」 を当てはめて、具体的なテスト項目に分解します。
このステップが、抜け漏れを潰すための核心部分です。

Step4:データは「同値分割+境界値」で代表値に圧縮する

すべての入力パターンを試すのは不可能です。そこで、テスト技法を使って効率的に代表値を選びます。

  • 同値分割法: 同じように扱われるデータの集合(同値パーティション)に分け、各集合から一つずつ代表値を選んでテストする。
  • 境界値分析: バグが最も発生しやすい「仕様の境界」とその周辺の値を重点的にテストする。

例:日付入力(1/1〜12/31)

  • 正常値(有効同値): 2024-04-01
  • 境界値: 2024-01-01, 2024-12-31, 2024-02-29(うるう年)
  • 異常値(無効同値): 2024-13-01, ABC, null, 2023-02-29(平年)

Step5:分岐ルールは「デシジョンテーブル」で漏れなく最小化

複数の条件が複雑に絡み合うロジックは、シナリオだけで追うと必ず漏れが出ます。
デシジョンテーブルを使って「条件」と「結果」の組み合わせを表にすることで、網羅性を担保しつつ、冗長なテストを削減できます。

Step6:状態を持つところは「状態遷移」で抜け漏れを潰す

注文ステータス(受付決済中確定)、申請フロー、ログイン状態など、時間経過やイベントによって状態が変わる機能には 状態遷移テスト が非常に有効です。

  • 状態遷移図/表を作成する。
  • すべての状態を少なくとも1回は経由するテストケースを作成する(状態網羅)。
  • すべての「矢印(遷移)」を少なくとも1回は経由するテストケースを作成する(遷移網羅)。
  • ありえないはずの遷移(例: 確定決済中)が発生しないことを確認する。

Step7:組合せ爆発は「ペアワイズ法」から始める

ブラウザ×OS×権限×支払方法…のように、パラメータの組み合わせが膨大な場合、全組み合わせをテストするのは不可能です。

NIST(米国国立標準技術研究所)の調査では、ソフトウェアのバグの多くは、1つまたは2つのパラメータの相互作用によって引き起こされると言われています。

そこで ペアワイズ法(All-Pairs Testing) という技法を使い、「パラメータのすべてのペア(2因子の組み合わせ)を最低1回はテストする」ように組み合わせを絞り込みます。これにより、テストケース数を劇的に削減しつつ、高い網羅性を維持できます。

補足:安全系・規制領域・重大障害が許されない領域では、2因子だけでは不足する可能性があります。
必要に応じて t-way(3-way以上) の組合せも検討するとより丁寧です。

4. ミニ例:ECサイトの「購入」機能でテストを組む

ここまでの手順を、ECサイトの購入機能に当てはめてみましょう。

4.1 コンポーネントと結合点(Step1)

  • UI → 注文API(入力バリデーション、二重送信防止)
  • 注文API → 認証API(セッショントークン、権限チェック)
  • 注文API → 在庫DB(在庫引き当て、排他制御)
  • 注文API → 決済GW(外部APIの成功/失敗/タイムアウト)
  • 注文API → メール送信バッチ(非同期連携、キューイング)

4.2 シナリオ(最小構成)(Step2)

  • 正常系: 会員がログインし、商品をカートに入れ、購入を完了する。
  • 異常系: カード決済に失敗した場合、注文ステータス、エラーメッセージ、在庫数が正しくハンドリングされるか。
  • 異常系: 購入ボタンを二重クリックしても、同一注文が二重に作成されないか。
  • 復旧系: 決済がタイムアウトした場合、注文がキャンセルされるか、あるいはリトライ可能な状態になるか。

4.3 データ設計(同値分割+境界値)(Step4)

例:クーポン割引率(0〜50%が有効)

  • 有効同値: 10
  • 境界値: 0, 50
  • 無効同値(境界外): -1, 51
  • 無効同値(型違い): ABC, null

4.4 条件分岐(デシジョンテーブル)(Step5)

「支払方法」「クーポン」「在庫」で結果が変わるロジックを表にします。

条件 R1 R2 R3 R4 R5
支払方法=カード Y Y Y N ...
クーポン=有り Y Y N Y ...
在庫=有り Y N Y Y ...
結果
注文確定 ...
在庫引当 ...
注文失敗 ...

これにより、「必要な組み合わせだけ」を効率的にテストできます。

4.5 状態遷移(注文ステータス)(Step6)

  • 作成決済中確定
  • 作成決済中失敗キャンセル
  • 作成決済中失敗 → (再決済) → 決済中確定

これらの遷移パスを網羅するテストケースを作成します。

5. 回帰(リグレッション)テストをどう最小化するか

機能追加や修正のたびに、全結合テストを再実行するのは非現実的です。回帰テストの目的は 「修正によって予期せぬ副作用(デグレード)が起きていないか」 を確認することです。

ここでも 前提(3)「欠陥は偏在する」 の考え方が役立ちます。影響範囲を以下の観点で絞り込み、リスクの高い箇所を優先的にテストするのが合理的です。

  • 変更箇所との関連が強いシナリオ
  • ビジネスインパクトが大きいシナリオ
  • ロジックが複雑なシナリオ
  • 過去に不具合が多発したシナリオ

6. すぐ使える:結合テストケースのテンプレート

最後に、ここまでの考え方を詰め込んだテストケースのテンプレートを紹介します。「観点チェック」欄を設けることで、抜け漏れ防止に繋がります。

### TC-ID:
IT-ORDER-001

### タイトル:
【正常系】クーポンを利用して商品を購入できること

### 目的(どの結合点/どのリスクを潰すか):
- 注文APIと決済GWの正常な連携を確認する。
- クーポン適用時の金額計算ロジックのリスクを潰す。

### 前提:
- ユーザーはログイン済みである。
- カートに商品が1つ入っている。

### 手順:
1. カート画面で有効なクーポンコードを入力する。
2. 注文確認画面で購入ボタンを押下する。
3. 決済GWの画面で有効なカード情報を入力し、決済を完了する。
4. 購入完了画面に遷移することを確認する。

### 入力データ(同値分割/境界値のどれか明記):
- クーポン割引率: 10%(有効同値)
- 支払方法: クレジットカード

### 期待結果(インターフェイス / 画面 / DB / 外部連携の観点で):
- **画面:**
  - 割引額、最終的な支払額が正しく表示される。
  - 購入完了画面に「注文が完了しました」と表示される。
- **DB:**
  - `orders`テーブルに、割引が適用された金額でレコードが作成される。ステータスは「確定」。
  - `stocks`テーブルの在庫数が1減少する。
- **外部連携:**
  - 決済GWに正しい金額で決済リクエストが送信されていることをログで確認。
  - メール送信キューに、購入完了通知のデータがエンキューされていることを確認。

### 観点チェック:
- [x] インターフェイス
- [x] 期待結果
- [x] データ
- [ ] エラー発生可能性

### 優先度(高/中/低)と理由:
- **高**
- 理由: ビジネスの根幹である購入フローであり、金額計算に関わるため。

### 備考:
- 自動化候補。
- 決済GWのログは `trace-id` で要確認。

おわりに

結合テストは、闇雲にケースを増やせば品質が上がるわけではありません。

  1. テストの役割と限界を理解する。
  2. リスクと欠陥の偏りを意識し、テストの濃淡をつける。
  3. **「観点」を固定し、「テスト技法」**を武器に、効率的なケースを設計する。

これらの考え方と手順が、あなたの「これで大丈夫だろうか…」という不安を、「このリスクは、このテストで潰した」という自信に変える一助となれば幸いです。


採用拡大中!

アシストエンジニアリングでは一緒に働くフロントエンド、バックエンドのエンジニア仲間を大募集しています!

少しでも興味ある方は、カジュアル面談からでもぜひお気軽にお話ししましょう!

お問い合わせはこちらから↓

https://official.assisteng.co.jp/contact/

参考リソース

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?