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?

単体テストのエッジケース・コーナーケース

Posted at

目次

  1. 基本定義と重要度基準
  2. 優先度別テストケースと期待結果
  3. テストケース具体例(コード実装付き)
  4. 優先度決定のためのリスク評価マトリクス
  5. 業種別優先テストケース一覧
  6. 生成AIとの効果的な協業アプローチ
  7. 現実的なテスト計画立案のためのステップ
  8. テスト担当者のための判断基準チェックリスト
  9. まとめ:現実的なアプローチのための原則

1. 基本定義と重要度基準

1.1 エッジケース (Edge Case)

単一の変数または条件が極端な値や境界値をとる状況。システムが正常に動作する範囲の「端(エッジ)」に位置するケース。

1.2 コーナーケース (Corner Case)

複数の変数や条件が同時に極端な値や特殊な状態になる状況。複数のエッジケースが同時に発生することで、さらに複雑な状態となるケース。

1.3 重要度の判断基準

重要度 判断基準
• ビジネスへの影響が重大(データ消失、金銭的損失、法的問題など)
• 障害発生時のシステム全体への波及効果が大きい
• 発生頻度が高いまたは発生の可能性が高い
• セキュリティやプライバシーに関わる問題
• 基本的な機能や処理フローに関わるケース
• 一部のユーザーや機能に影響があるが限定的
• 回避策や代替手段が存在する
• 発生頻度は中程度
• 運用で対処可能だが自動化が望ましいもの
• パフォーマンスに関わるがクリティカルではないケース
• 影響が軽微または美観・使い勝手のみに関わる
• 非常に特殊な条件下でのみ発生
• 発生頻度が極めて低い
• 簡単な回避策が存在する
• システム拡張や将来の変更に関わるケース

2. 優先度別テストケースと期待結果

2.1 数値関連

重要度 テストケース 期待結果 理由
最大値処理 最大値を適切に処理する。オーバーフロー時は明確なエラーを返す 境界値処理の基本。多くのバグがこの境界で発生
最小値処理 最小値を適切に処理する。アンダーフロー時は明確なエラーを返す 境界値処理の基本。負の値の処理で問題が発生しやすい
ゼロ値処理 ゼロを適切に処理する。ゼロ除算時は例外をスローまたはエラーを返す 除算やパラメータとして特別な意味を持つことが多い
負の値処理 負の値を正しく処理するか、不許可の場合は適切なエラーメッセージを表示 負の値を想定していない処理で問題発生しやすい
小数点以下の精度と丸め 定義された精度と丸めルールに従って正確に計算される 金融計算など高精度が要求される場合は重要度高
非常に大きな/小さな値 システム制限内なら処理成功。制限外なら適切なエラーメッセージを表示 パフォーマンス問題や精度問題の原因になりうる
NaN/Infinity処理 NaN/Infinityを検出し、適切に対応(エラーまたは代替値) 特定の計算処理で発生し、後続処理に影響
型変換境界 型変換で情報喪失がある場合は警告または例外。維持できる場合は正確に変換 異なる数値型間の変換で精度損失の可能性
特殊な計算値(素数、累乗等) 計算アルゴリズムが特殊値でも正確に動作する 特定アルゴリズムのみで重要

2.2 文字列関連

重要度 テストケース 期待結果 理由
空文字列処理 空文字列を適切に処理。NULLと区別して扱う NULL/空の区別や処理が多くのバグの原因
最大長文字列 最大長まで処理成功。超過時は明確なエラーメッセージを表示 バッファオーバーフローやパフォーマンス問題の原因
SQLインジェクション文字 特殊文字をエスケープまたはパラメータ化し、インジェクション攻撃を防止 セキュリティ脆弱性に直結
HTML/スクリプトタグ タグをエスケープして表示。XSS攻撃を防止する XSS脆弱性のリスク
マルチバイト/Unicode文字 文字化けなく正しく表示・処理。文字数カウントが正確 文字化けや長さ計算ミスの原因
制御文字を含む文字列 制御文字を適切に処理または除去。表示崩れを防止 表示や処理に予期せぬ影響を与える可能性
絵文字を含む文字列 絵文字を正しく表示・保存。文字数カウントが正確 モバイルアプリでは重要度が上がる
非常に長い単語/改行なし文字列 レイアウト崩れなく表示。必要に応じて折り返し/省略表示 UIレイアウト崩れの原因
右から左へ記述する言語 文字方向を正しく処理し、レイアウト崩れなく表示 国際化対応時には重要度が上がる

2.3 日付・時間関連

重要度 テストケース 期待結果 理由
日付境界値(月末、年末) 月末日から月加算時に適切な日付調整(30日→31日、31日→30日など) 日付計算バグが最も発生しやすい
閏年(2月29日) 閏年の2月29日を正しく認識・処理。閏年ルールを正確に適用 4年ごとの特殊処理でエラーが発生しやすい
タイムゾーン処理 タイムゾーン間の変換が正確。日付変更線をまたぐ計算が正確 グローバルシステムでは特に重要
日付形式変換 様々な日付形式(YYYY/MM/DD、MM/DD/YYYY等)を正しく解析・変換 地域による形式の違いが問題になりうる
夏時間/冬時間切替日 存在しない/重複する時間を正しく処理。時間計算が正確 存在しない時間や重複する時間が発生
存在しない日付(2月30日等) 不正な日付を検出し、適切なエラーメッセージを表示 ユーザー入力バリデーションで重要
日付範囲の端(最古/最新) システムの対応日付範囲内なら処理成功。範囲外ならエラー表示 データ型の限界に関わる
うるう秒 うるう秒を含む正確な時刻計算。時刻同期問題を回避 超高精度タイミングが必要なシステムのみ
歴史的な日付変更 暦の改正などによる歴史的な日付変更を正しく処理 歴史データを扱うシステムのみ

2.4 データベース関連

重要度 テストケース 期待結果 理由
トランザクション整合性 全処理が成功するか、全処理が元に戻る(全部か無か) データ不整合によるビジネス影響が大きい
同時実行・ロック競合 ロック競合を検出し、デッドロックを回避。適切なエラーメッセージを返す 高負荷時のパフォーマンス・整合性に影響
大量データ処理 メモリ消費を抑えつつ大量データを効率的に処理。タイムアウトなし 本番環境での性能問題に直結
一意制約違反 一意制約違反を検出し、適切なエラーメッセージを表示。部分更新なし データ重複によるビジネスロジック問題
外部キー制約 参照整合性違反を検出し、適切なエラーまたはカスケード処理 参照整合性エラーによるデータ問題
NULL値の扱い NULL値を適切に処理。NOT NULL制約に違反するとエラー表示 検索や計算結果の不正確さの原因
長時間トランザクション 長時間トランザクションを検出し、タイムアウト処理またはリソース解放 リソースロックやタイムアウトの原因
複雑なJOIN操作 複雑なJOINでもデータ欠落なく正確に結果を返す パフォーマンスとデータ整合性に影響
データ型の境界値 各データ型の最大/最小値を正しく処理または適切なエラー ストレージやパフォーマンスに限定的影響
文字セット・照合順序 文字セットに応じた正確な並べ替えと比較。文字化けなし 特定言語処理では重要度が上がる

2.5 UI・ユーザー入力関連

重要度 テストケース 期待結果 理由
不正な形式の入力 入力形式チェックでエラー検出。適切なガイダンスを表示 バリデーション不備がセキュリティ問題に
必須入力の欠落 必須項目の欠落を検出し、送信を阻止。エラー箇所を明示 データ不整合の原因
ブラウザ互換性 主要ブラウザでレイアウト崩れなく正常動作 主要ブラウザでの動作保証は必須
入力文字数超過 最大文字数超過を検出し、適切なメッセージ表示または自動切り詰め データ切り捨てや処理エラーの原因
画面サイズ・解像度 様々な画面サイズでコンテンツが適切に表示される レスポンシブ設計の検証
アクセシビリティ要件 スクリーンリーダー対応。キーボード操作のみでナビゲーション可能 法的要件の場合は重要度が上がる
入力遅延・タイミング ユーザー入力に対するレスポンスが適切な時間内(< 100ms) UX問題の原因
複数入力の同時処理 複数フィールドの依存関係を正しく処理。整合性チェック実施 特殊なUIのみで重要
クリップボードからの大量ペースト 大量テキストのペーストを適切に処理。必要に応じて分割処理 特定のフォーム入力で問題になりうる

2.6 ネットワーク関連

重要度 テストケース 期待結果 理由
ネットワーク切断 切断を検出し、適切なエラーメッセージを表示。可能なら再接続を試行 モバイルアプリやWifi環境では頻発
タイムアウト処理 適切なタイムアウト値を設定。タイムアウト時はユーザーに通知 外部連携システムで特に重要
不完全なレスポンス 不完全なレスポンスを検出し、適切にエラー処理または再試行 エラー処理不備でシステム障害に
レスポンスなし 無限待機を回避。適切なタイムアウトとエラー表示 ハングや無限待機の原因
高レイテンシ環境 高遅延環境でもUI応答性を維持。処理中表示を適切に表示 ユーザー体験やタイムアウトに影響
帯域制限 低帯域幅環境でもコアな機能が動作。必要に応じて軽量モード提供 大容量データ転送時に問題
パケットロス パケット損失を検出し、適切に再送信。データ整合性を維持 モバイル環境では重要度が上がる
ネットワークジッター レイテンシ変動を吸収し、一貫したユーザー体験を提供 リアルタイム通信でのみ重要
MTU制限 大きなデータのフラグメント転送が正常に動作 特殊なネットワーク環境のみ

2.7 並行処理関連

重要度 テストケース 期待結果 理由
デッドロック デッドロックを検出・回避。発生時は適切なエラーを返す システム全体のハングアップの原因
競合状態(Race Condition) 競合状態を検出し排他制御。データ整合性を維持 データ不整合や予測不能な動作の原因
リソース枯渇 リソース上限に達した際は適切にエラー処理。追加リクエストは拒否 サービス停止に直結
部分的失敗 部分処理の失敗を検出し、全体を元に戻すか明確なエラー状態に遷移 トランザクション全体の整合性に影響
排他制御 悲観的/楽観的ロックが正しく機能。更新競合を検出して対応 データ整合性とパフォーマンスのバランス
同期ブロック内の例外 同期ブロック内の例外発生時にもリソースを適切に解放 リソースリークの原因
スレッドプール枯渇 プール枯渇時は新規タスクをキューイングまたは拒否。監視機能あり 徐々に性能低下を引き起こす
優先度逆転 優先度逆転問題を検出・防止する仕組みが機能する リアルタイムシステムでのみ重要
再帰的ロック 再帰的ロックが適切に動作。デッドロックを引き起こさない 特定の実装パターンでのみ問題

2.8 セキュリティ関連

重要度 テストケース 期待結果 理由
認証バイパス 認証のバイパス試行を検出し阻止。セキュリティログに記録 不正アクセスに直結
権限昇格 権限昇格の試みを検出し阻止。セキュリティログに記録 より高い権限の不正取得リスク
セッションハイジャック セッションの盗用を検出し阻止。セッション固定化攻撃を防止 なりすましの危険性
データ漏洩 機密データが適切に保護され、不正アクセスから隔離されている 個人情報漏洩等の重大事故リスク
入力検証バイパス クライアント側検証バイパスを検出し、サーバー側で確実に検証 多くの攻撃の起点となる
クロスサイトリクエスト CSRF対策が機能し、偽装リクエストを検出・拒否 CSRF対策の検証
中間者攻撃 適切な暗号化と証明書検証により中間者攻撃を防止 通信セキュリティの検証
リプレイ攻撃 ワンタイムトークンや有効期限チェックによりリプレイ攻撃を防止 トークン再利用等のリスク
辞書攻撃 パスワード強度要件とレート制限によりブルートフォース攻撃を防止 ブルートフォース対策と併せて検証

2.9 ビジネスロジック関連

重要度 テストケース 期待結果 理由
業務フロー例外パス 通常と異なるフロー(差戻し、取消など)が正しく処理される 通常と異なるフローの正確性検証
計算精度と丸め処理 金額計算が指定された丸めルールに従って正確に行われる 金融等で特に重要
業務カレンダー例外日 休日・特別営業日が正しく認識され、適切に処理される 休日・特別営業日等の対応
マスタ・トランザクション連携 マスタ変更がトランザクションに正しく反映される データ整合性の基盤
権限境界処理 権限境界上の操作(承認権限の委譲など)が正しく処理される 組織改編等での処理継続性
バージョン移行時の互換性 システム更新時に過去データが正しく処理される システム更新時の業務継続性
過去データ修正影響 過去データ修正が後続処理に適切に反映される 履歴データへの波及効果
締め処理・期末処理 月次/年次締め処理が正しく実行され、次期間に移行する 期限付き処理の正確性
特殊顧客・取引の扱い VIP顧客や特殊取引に対する例外ルールが正しく適用される VIP対応等の例外処理
外部システム連携障害時対応 外部システム障害時に適切な代替処理または明確なエラー表示 代替処理の可否検証

3. テストケース具体例(コード実装付き)

3.1 高優先度テスト具体例

最大値境界テスト(数値関連)

テストケース:

  • 最大整数値(Integer.MAX_VALUE)での計算
  • 最大整数値を超える計算

期待結果:

  • 最大値ちょうどの計算は正常に処理される
  • 最大値を超える計算は適切に例外をスローするか、エラー処理される

コード実装例:

@Test
public void testMaxValueProcessing() {
    // 最大値ちょうどの処理
    Integer maxValue = Integer.MAX_VALUE;
    Integer result = calculationService.processValue(maxValue);
    assertEquals(maxValue, result, "最大値が正しく処理されるべき");
    
    // 最大値を超える処理
    assertThrows(ValueOverflowException.class, () -> {
        calculationService.add(maxValue, 1);
    }, "最大値を超える計算は例外をスローすべき");
    
    // BigDecimalでの大きな数値計算
    BigDecimal largeValue = new BigDecimal("10000000000000000000");
    BigDecimal calculated = financialService.calculateInterest(largeValue, new BigDecimal("0.05"));
    assertEquals(new BigDecimal("500000000000000000.00"), calculated, "大きな数値の計算も正確であるべき");
}

トランザクション整合性テスト(データベース関連)

テストケース:

  • 複数テーブル更新の途中でエラー発生

期待結果:

  • トランザクションが完全にロールバックされ、すべてのテーブルが元の状態に戻る

コード実装例:

@Test
public void testTransactionIntegrity() {
    // 前提条件の確認
    Account sourceAccount = accountRepository.findById(1L).get();
    Account targetAccount = accountRepository.findById(2L).get();
    
    BigDecimal initialSourceBalance = sourceAccount.getBalance();
    BigDecimal initialTargetBalance = targetAccount.getBalance();
    
    // 転送金額
    BigDecimal transferAmount = new BigDecimal("1000.00");
    
    try {
        // 転送中にエラー発生を強制
        transferService.transferWithError(
            sourceAccount.getId(), 
            targetAccount.getId(),
            transferAmount
        );
        fail("例外が発生すべき");
    } catch (RuntimeException e) {
        // 期待通りの例外発生
    }
    
    // DBから再取得して確認
    sourceAccount = accountRepository.findById(1L).get();
    targetAccount = accountRepository.findById(2L).get();
    
    // 両方のアカウントが元の残高に戻っているか検証(ロールバック確認)
    assertEquals(initialSourceBalance, sourceAccount.getBalance(), 
                "エラー発生時は送金元口座の残高が元に戻るべき");
    assertEquals(initialTargetBalance, targetAccount.getBalance(), 
                "エラー発生時は送金先口座の残高が変わらないべき");
}

認証バイパステスト(セキュリティ関連)

テストケース:

  • 認証なしで保護リソースへのアクセス試行
  • 期限切れトークンでのアクセス試行
  • 不正なロールでのアクセス試行

期待結果:

  • すべてのバイパス試行が拒否され、適切なエラーステータス(401/403)が返される
  • セキュリティログにアクセス試行が記録される

コード実装例:

@Test
public void testAuthenticationBypassPrevention() {
    // 認証なしアクセス
    MockHttpServletResponse response = mockMvc.perform(
        get("/api/secured/resource")
    ).andReturn().getResponse();
    
    assertEquals(HttpStatus.UNAUTHORIZED.value(), response.getStatus(), 
                "認証なしアクセスは401を返すべき");
    
    // 期限切れトークン
    String expiredToken = generateExpiredToken(USER_ID);
    response = mockMvc.perform(
        get("/api/secured/resource")
        .header("Authorization", "Bearer " + expiredToken)
    ).andReturn().getResponse();
    
    assertEquals(HttpStatus.UNAUTHORIZED.value(), response.getStatus(), 
                "期限切れトークンは401を返すべき");
    
    // 不正ロールによるアクセス
    String validTokenWithWrongRole = generateTokenWithRole(USER_ID, "GUEST");
    response = mockMvc.perform(
        get("/api/admin/resource")
        .header("Authorization", "Bearer " + validTokenWithWrongRole)
    ).andReturn().getResponse();
    
    assertEquals(HttpStatus.FORBIDDEN.value(), response.getStatus(), 
                "権限不足アクセスは403を返すべき");
    
    // セキュリティログの検証
    List<SecurityLogEntry> logs = securityLogRepository.findByUserIdAndAction(
        USER_ID, SecurityAction.UNAUTHORIZED_ACCESS_ATTEMPT);
    assertTrue(logs.size() >= 2, "不正アクセス試行がログに記録されるべき");
}

3.2 中優先度テスト具体例

閏年処理テスト(日付・時間関連)

テストケース:

  • 閏年(2月29日)から1年後の日付計算
  • 閏年でない年の2月29日の処理

期待結果:

  • 閏年の2月29日から1年後は2月28日になる
  • 閏年でない年の2月29日は無効日付として扱われエラーになる

コード実装例:

@Test
public void testLeapYearDateHandling() {
    // 閏年(2024年)の2月29日
    LocalDate leapYearDate = LocalDate.of(2024, 2, 29);
    assertTrue(dateService.isValidDate(leapYearDate), "閏年の2月29日は有効な日付");
    
    // 1年後(閏年でない年の2月29日相当)
    LocalDate oneYearLater = dateService.addYears(leapYearDate, 1);
    assertEquals(LocalDate.of(2025, 2, 28), oneYearLater, 
                "閏年の2月29日から1年後は2月28日になるべき");
    
    // 閏年でない年の2月29日
    LocalDate invalidDate = LocalDate.of(2023, 2, 29);
    assertThrows(DateTimeException.class, () -> {
        dateService.isValidDate(invalidDate);
    }, "閏年でない年の2月29日は無効な日付として例外をスローすべき");
}

並行処理テスト(並行処理関連)

テストケース:

  • 複数スレッドから同一リソースへの同時更新

期待結果:

  • 楽観的ロックにより競合を検出し、適切に例外を発生させる
  • または悲観的ロックにより順序付けられた処理が行われる

コード実装例:

@Test
public void testConcurrentResourceUpdate() throws InterruptedException {
    // テスト用の共有リソース
    final Long productId = 42L;
    final int initialStock = 10;
    
    // 初期状態設定
    productRepository.updateStock(productId, initialStock);
    
    // 複数スレッドからの同時アクセスをシミュレーション
    final int threadCount = 5;
    final CountDownLatch startLatch = new CountDownLatch(1);
    final CountDownLatch endLatch = new CountDownLatch(threadCount);
    
    final AtomicInteger successCount = new AtomicInteger(0);
    final AtomicInteger conflictCount = new AtomicInteger(0);
    
    for (int i = 0; i < threadCount; i++) {
        new Thread(() -> {
            try {
                startLatch.await(); // すべてのスレッドを同時スタート
                try {
                    stockService.decrementStock(productId, 2); // 在庫を2減らす
                    successCount.incrementAndGet();
                } catch (OptimisticLockException e) {
                    conflictCount.incrementAndGet();
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                endLatch.countDown();
            }
        }).start();
    }
    
    startLatch.countDown(); // 全スレッド開始
    endLatch.await(); // 全スレッド完了待ち
    
    // 楽観的ロックの場合:一部成功、一部コンフリクト
    assertTrue(successCount.get() > 0, "一部のスレッドは成功すべき");
    assertTrue(conflictCount.get() > 0, "一部のスレッドは競合検出されるべき");
    
    // 最終的な在庫数を検証
    int finalStock = productRepository.getStock(productId);
    assertEquals(initialStock - (2 * successCount.get()), finalStock, 
                "最終在庫数は正確であるべき");
}

マルチバイト文字処理テスト(文字列関連)

テストケース:

  • 日本語、中国語、アラビア語などのマルチバイト文字を含む文字列処理
  • UTF-8とShift-JISの文字コード変換

期待結果:

  • 文字化けなく正しく処理される
  • 文字数カウントが正確(バイト数と文字数の区別)

コード実装例:

@Test
public void testMultiByteStringProcessing() {
    // マルチバイト文字の文字列
    String japaneseTxt = "こんにちは世界"; // 7文字
    String chineseTxt = "你好世界";      // 4文字
    String arabicTxt = "مرحبا بالعالم";   // 13文字(空白含む)
    
    // 文字数カウントの正確性
    assertEquals(7, stringUtil.countCharacters(japaneseTxt), 
                "日本語の文字数が正しくカウントされるべき");
    assertEquals(4, stringUtil.countCharacters(chineseTxt), 
                "中国語の文字数が正しくカウントされるべき");
    assertEquals(13, stringUtil.countCharacters(arabicTxt), 
                "アラビア語の文字数が正しくカウントされるべき");
    
    // 文字コード変換の正確性
    byte[] utf8Bytes = japaneseTxt.getBytes(StandardCharsets.UTF_8);
    byte[] shiftJisBytes = stringUtil.convertToShiftJIS(japaneseTxt);
    
    String convertedBack = stringUtil.convertFromShiftJIS(shiftJisBytes);
    assertEquals(japaneseTxt, convertedBack, 
                "エンコード/デコード後も文字列が一致するべき");
    
    // バイト長と文字数の区別
    assertTrue(utf8Bytes.length > japaneseTxt.length(), 
               "UTF-8のバイト長は文字数より大きいべき");
    assertTrue(shiftJisBytes.length > japaneseTxt.length(), 
               "Shift-JISのバイト長は文字数より大きいべき");
}

3.3 低優先度テスト具体例

特殊顧客ルールテスト(ビジネスロジック関連)

テストケース:

  • VIP顧客に対する特別割引ルールの適用
  • 長期顧客に対する特典適用
  • 特別な日付(記念日、誕生日)の優遇処理

期待結果:

  • 特殊ルールが正しく適用され、適切な割引や特典が計算される

コード実装例:

@Test
public void testSpecialCustomerRules() {
    // VIP顧客の設定
    Customer vipCustomer = new Customer();
    vipCustomer.setId(100L);
    vipCustomer.setCustomerRank(CustomerRank.VIP);
    vipCustomer.setRegistrationDate(LocalDate.now().minusYears(5)); // 5年前に登録
    vipCustomer.setBirthdate(LocalDate.now()); // 今日が誕生日
    
    // 通常の注文
    Order regularOrder = new Order();
    regularOrder.setCustomer(vipCustomer);
    regularOrder.setOrderDate(LocalDate.now());
    regularOrder.setTotalAmount(new BigDecimal("10000"));
    
    // 割引適用
    Order discountedOrder = specialRuleService.applySpecialRules(regularOrder);
    
    // 期待される割引
    // - VIP割引: 10%
    // - 5年以上の長期顧客割引: 5%
    // - 誕生日特典: 1000円割引
    BigDecimal expectedAmount = new BigDecimal("10000")
        .multiply(new BigDecimal("0.90")) // VIP割引
        .multiply(new BigDecimal("0.95")) // 長期顧客割引
        .subtract(new BigDecimal("1000")) // 誕生日割引
        .setScale(0, RoundingMode.HALF_UP);
    
    assertEquals(expectedAmount, discountedOrder.getTotalAmount(), 
                "特別割引ルールが正しく適用されるべき");
    
    // 割引適用理由の記録確認
    List<Discount> appliedDiscounts = discountedOrder.getAppliedDiscounts();
    assertEquals(3, appliedDiscounts.size(), "3つの割引が適用されるべき");
    
    boolean hasVipDiscount = appliedDiscounts.stream()
        .anyMatch(d -> d.getType() == DiscountType.VIP);
    boolean hasLoyaltyDiscount = appliedDiscounts.stream()
        .anyMatch(d -> d.getType() == DiscountType.LOYALTY);
    boolean hasBirthdayDiscount = appliedDiscounts.stream()
        .anyMatch(d -> d.getType() == DiscountType.BIRTHDAY);
    
    assertTrue(hasVipDiscount, "VIP割引が適用されるべき");
    assertTrue(hasLoyaltyDiscount, "長期顧客割引が適用されるべき");
    assertTrue(hasBirthdayDiscount, "誕生日割引が適用されるべき");
}

将来拡張性テスト(拡張性関連)

テストケース:

  • 現在の10倍のデータ量でのパフォーマンス予測
  • 新しいビジネスルールの動的追加

期待結果:

  • 大量データでも許容可能なパフォーマンスを維持
  • 新ルールを既存コードを変更せずに追加可能

コード実装例:

@Test
public void testSystemScalability() {
    // 現在の10倍のユーザー数をシミュレーション
    int futureUserCount = currentUserCount * 10;
    List<User> simulatedUsers = generateSimulatedUsers(futureUserCount);
    
    // パフォーマンス計測
    long startTime = System.currentTimeMillis();
    userService.processMonthlyReports(simulatedUsers);
    long endTime = System.currentTimeMillis();
    
    // 処理時間を現在のユーザー数での処理時間と比較
    long processingTime = endTime - startTime;
    long timePerUser = processingTime / futureUserCount;
    
    // 処理時間は線形またはそれ以下であるべき(O(n)またはO(log n))
    assertTrue(timePerUser <= currentTimePerUser * 1.5, 
              "ユーザー数増加時も1ユーザーあたりの処理時間が大幅に増加しないこと");
    
    // メモリ使用量が許容範囲内か
    Runtime runtime = Runtime.getRuntime();
    long usedMemory = runtime.totalMemory() - runtime.freeMemory();
    long averageMemoryPerUser = usedMemory / futureUserCount;
    
    assertTrue(averageMemoryPerUser <= maxAcceptableMemoryPerUser, 
              "ユーザーあたりのメモリ消費が許容範囲内であること");
}

@Test
public void testBusinessRuleExtensibility() {
    // 動的なビジネスルール追加テスト
    // テスト用の新ルールを作成
    BusinessRule newRule = new BusinessRule() {
        @Override
        public String getName() {
            return "TestSpecialHolidayRule";
        }
        
        @Override
        public boolean isApplicable(Order order) {
            return order.getOrderDate().getMonth() == Month.DECEMBER && 
                   order.getOrderDate().getDayOfMonth() == 25;
        }
        
        @Override
        public void apply(Order order) {
            // クリスマス特別割引: 20%
            BigDecimal discount = order.getTotalAmount().multiply(new BigDecimal("0.20"));
            order.applyDiscount(discount, "Christmas Special");
        }
    };
    
    // 既存ルールエンジンに新ルールを追加
    ruleEngine.addRule(newRule);
    
    // クリスマスの注文
    Order christmasOrder = new Order();
    christmasOrder.setOrderDate(LocalDate.of(2023, 12, 25));
    christmasOrder.setTotalAmount(new BigDecimal("10000"));
    
    // ルール適用
    ruleEngine.applyRules(christmasOrder);
    
    // 新ルールが正しく適用されたか検証
    assertEquals(new BigDecimal("8000.00"), christmasOrder.getTotalAmount(), 
                "クリスマス特別割引が適用されるべき");
    
    // ルール削除の検証
    ruleEngine.removeRule("TestSpecialHolidayRule");
    
    Order anotherOrder = new Order();
    anotherOrder.setOrderDate(LocalDate.of(2023, 12, 25));
    anotherOrder.setTotalAmount(new BigDecimal("10000"));
    
    ruleEngine.applyRules(anotherOrder);
    
    // ルール削除後は適用されないはず
    assertEquals(new BigDecimal("10000.00"), anotherOrder.getTotalAmount(), 
                "削除されたルールは適用されないべき");
}

4. 優先度決定のためのリスク評価マトリクス

以下のマトリクスを使用して、各テストケースの優先度を評価・決定します。

4.1 リスク評価マトリクス

影響度\発生確率
(重大な障害、データ損失、セキュリティ侵害) 高優先度 高優先度 中優先度
(部分的機能停止、パフォーマンス低下) 高優先度 中優先度 低優先度
(UI問題、軽微な不具合) 中優先度 低優先度 低優先度

4.2 優先度別リソース配分の目安

優先度 リソース配分 テスト自動化 実施頻度
60% 必須 毎回のリリース
30% 推奨 主要リリース・定期的
10% 任意 影響範囲時・時間許す限り

5. 業種別優先テストケース一覧

5.1 金融システム優先テストケース

重要度 テストケース 期待結果
計算精度と丸め処理 税率・利息計算が正確。端数処理が一貫している
取引データ整合性 二重計上なし。残高不足時は処理中断
マネーロンダリング検知 疑わしい取引パターンを検出し、フラグを設定
期日・締め処理 月末・年度末処理が正確。休業日自動調整
監査証跡 すべての金融取引が正確に記録され、改ざん不能
税率変更対応 税率変更日を跨ぐ処理が正しく適用される
為替レート変動 為替レート変動時も計算が正確。許容範囲超過時は承認要求
口座凍結処理 凍結口座への入出金が適切に制限される

5.2 ECサイト優先テストケース

重要度 テストケース 期待結果
在庫管理 在庫ゼロ時は購入不可。同時購入競合を検出
支払処理 決済エラー時はカート状態維持。タイムアウト検出
大規模セール 高負荷時もサイト応答性維持。価格整合性確保
カート保持 セッション切れでもカート内容を回復可能
配送オプション 配送不可地域は選択不可。日時指定の整合性確保
割引コード適用 複数割引の組み合わせルールが正しく適用される
返品処理 返品による在庫・金額の反映が正確
商品レコメンド パーソナライズされた推奨商品が表示される

5.3 医療システム優先テストケース

重要度 テストケース 期待結果
患者データ整合性 患者IDの一意性確保。検査結果の正確な紐付け
処方安全性 薬剤相互作用を検出。用量範囲をチェック
アレルギー警告 患者アレルギーと禁忌薬剤の照合が確実
緊急対応 緊急モードでのアクセス制御と記録が適切
検査結果表示 臨界値の強調表示。時系列での変化表示
匿名化処理 研究用データの適切な匿名化と保護
保険請求処理 保険適用ルールの正確な適用と請求額計算
医療機器連携 各種医療機器からのデータ取込みが正確

5.4 製造業システム優先テストケース

重要度 テストケース 期待結果
材料所要量計算 BOM展開が正確。必要量と在庫の計算が正確
生産スケジューリング リソース制約を考慮した実行可能なスケジュール生成
品質管理基準 品質検査結果による合否判定が正確
トレーサビリティ ロット・シリアル管理による製品追跡が可能
設備稼働監視 異常値の検出と警告。メンテナンス予測が適切
作業指示生成 正確な作業指示と必要図面・文書の紐付け
廃棄物管理 廃棄物発生量の正確な記録と適切な処理方法選択
コスト計算 原価計算の正確性。原価差異の分析と表示

6. 生成AIとの効果的な協業アプローチ

6.1 優先度別の生成AI活用プロンプト例

高優先度テスト生成プロンプト

「[システム名]の[機能名]における高優先度テストケースを生成してください。
特に以下の要素を含めてください:

1. 境界値テスト(最大値、最小値、ゼロ値など)
2. トランザクション整合性テスト(複数テーブル更新時の例外発生ケース)
3. セキュリティ関連テスト(認証、権限チェック)
4. 各テストケースの期待結果を明確に記述

また、[過去に発生した重大バグの説明]に類似した問題を検出できるテストも含めてください。
テストコードは[言語名]で記述し、JUnitまたは同等のテストフレームワーク形式で提供してください。」

中優先度テスト生成プロンプト

「[システム名]の[機能名]における中優先度のエッジケースとコーナーケースを10件提案してください。
特に以下の観点を考慮してください:

1. 日付・時間関連の特殊ケース(閏年、月末日、タイムゾーンなど)
2. 国際化対応の検証(マルチバイト文字、言語固有の処理)
3. 並行処理における競合状態の検出
4. ネットワーク状況の変動への対応

各テストケースについて、テスト条件と期待される結果を明記してください。
可能であれば実装例もJavaまたはPythonで示してください。」

低優先度テスト生成プロンプト

「[システム名]の長期的な保守性と拡張性を検証するための低優先度テストケースを提案してください。
特に以下の観点を考慮してください:

1. 将来のデータ増加(現在の10倍)に対する処理性能の予測
2. 新しいビジネスルールや要件の追加容易性
3. 特殊な顧客・取引パターンへの対応
4. レアケースでのユーザー体験の検証

各テストケースについて、現時点でのリスク・影響度と、テスト実施の指針を提供してください。
特に優先して実施すべきケースには理由を付してください。」

6.2 生成AIによるテストデータ生成

優れたテストには多様で現実的なテストデータが必要です。生成AIを活用して様々なシナリオに適したテストデータを生成できます。

テストデータ生成プロンプト例

「顧客データのテストセットを生成してください。以下の条件を満たすJSON形式のデータ10件が必要です:

1. 顧客ID: 一意の数値(10000-99999)
2. 氏名: 日本人名の姓名
3. 生年月日: 1950年〜2005年の範囲
4. 住所: 日本の住所形式(郵便番号、都道府県、市区町村、番地)
5. メールアドレス: 有効な形式
6. 電話番号: 日本の電話番号形式
7. 顧客ランク: 'REGULAR', 'SILVER', 'GOLD', 'PLATINUM'のいずれか
8. 登録日: 2010年〜現在の範囲

以下の特殊ケースを含めてください:
- 同じ日に登録された2名
- 名前に特殊文字(ー、「」など)を含む人
- 住所に長い地名を含む人
- 最高ランクと最低ランクの顧客

データはJSONの配列形式で提供してください。」

6.3 テスト結果の分析への活用

テスト実行結果の分析も生成AIの得意分野です。特にパターン認識や傾向分析に活用できます。

テスト結果分析プロンプト例

「以下のテスト実行結果のログを分析し、主要な問題点と改善提案を提示してください:

[テスト実行ログの内容をここに貼り付け]

特に以下の観点で分析してください:
1. 最も頻繁に失敗しているテストケースのパターン
2. 実行時間が長いテストケースとその最適化提案
3. 関連性の高い複数のテスト失敗(同一の根本原因の可能性)
4. 環境依存の失敗(特定環境でのみ発生する問題)

分析結果をカテゴリ別に整理し、優先的に対応すべき項目を明記してください。」

7. 現実的なテスト計画立案のためのステップ

限られたリソースで効果的なテストを実施するための具体的ステップを以下に示します。

7.1 フェーズ1: 計画と優先順位付け(所要時間: 全体の15%)

ステップ 活動内容 成果物
リスク分析 機能別の影響度と発生確率を評価 リスクマトリクス
優先度設定 各テストケースをH/M/Lに分類 優先度付きテスト一覧
リソース配分 優先度に応じたリソース配分計画 テスト実施計画書
自動化方針 自動化すべきテストの選定 自動化対象リスト

7.2 フェーズ2: 高優先度テスト実施(所要時間: 全体の50%)

ステップ 活動内容 成果物
基盤テスト 基本機能と重要パスの検証 テスト結果報告書
境界値テスト 数値・日付・文字列の境界値検証 境界値テスト結果
整合性テスト トランザクション整合性とデータ整合性 整合性テスト結果
セキュリティテスト 認証・権限・脆弱性の検証 セキュリティテスト結果

7.3 フェーズ3: 中優先度テスト実施(所要時間: 全体の25%)

ステップ 活動内容 成果物
拡張機能テスト 主要機能の拡張部分検証 拡張機能テスト結果
環境差異テスト 複数環境での動作検証 環境互換性レポート
性能テスト 一般的な負荷状況での性能検証 性能測定結果
国際化テスト 多言語・地域設定の検証 国際化対応レポート

7.4 フェーズ4: 低優先度テスト実施(所要時間: 全体の10%)

ステップ 活動内容 成果物
特殊ケーステスト 稀なビジネスシナリオの検証 特殊ケーステスト結果
拡張性テスト 将来的な拡張に対する検証 拡張性評価レポート
UX検証 使い勝手と表示品質の検証 UX評価レポート
探索的テスト 自由形式の探索的テスト 発見事項リスト

8. テスト担当者のための判断基準チェックリスト

テスト担当者が日々の業務で使える簡潔なチェックリストです。

8.1 テストケース優先度判断の5つの質問

  1. 影響度: このケースが失敗した場合のビジネス影響は?

    • 重大(データ損失、金銭的損害、法的問題)→ 高優先度
    • 中程度(機能停止、パフォーマンス低下)→ 中優先度
    • 軽微(見た目の問題、軽微な不便)→ 低優先度
  2. 発生確率: 実際に問題が発生する可能性はどの程度か?

    • 高い(通常操作で発生)→ 優先度を1段階上げる
    • 中程度(特定条件で発生)→ 優先度維持
    • 低い(極めて特殊な条件でのみ発生)→ 優先度を1段階下げる
  3. 回避可能性: 問題発生時に回避策や代替手段はあるか?

    • 回避不能 → 優先度を1段階上げる
    • 煩雑だが回避可能 → 優先度維持
    • 容易に回避可能 → 優先度を1段階下げる
  4. 影響範囲: 問題の影響範囲はどの程度か?

    • システム全体 → 優先度を1段階上げる
    • 特定の機能グループ → 優先度維持
    • 単一機能の一部 → 優先度を1段階下げる
  5. ビジネス価値: 関連機能のビジネス価値は?

    • 主要収益源・コア機能 → 優先度を1段階上げる
    • 標準機能 → 優先度維持
    • 補助的機能 → 優先度を1段階下げる

8.2 時間制約下での効率的テスト実施指針

  1. 高優先度テストを100%実施: まず高優先度テストをすべて実施
  2. 自動化重視: 繰り返し実施するテストは自動化を優先
  3. 類似テストのグループ化: 関連するテストをまとめて効率化
  4. 探索的テスト活用: 経験則に基づく探索的テストで効率的に欠陥発見
  5. 変更影響分析: 変更箇所とその影響範囲に集中したテスト実施
  6. 早期フィードバック: 重要な問題を早期に発見するためのスモークテスト実施
  7. リスクベース減量: 時間不足の場合は低リスク項目から省略

9. まとめ:現実的なアプローチのための原則

  1. 完璧を求めない: すべてのエッジケース・コーナーケースをテストすることは現実的でない
  2. リスクベース思考: 影響度と発生確率に基づく優先順位付けを徹底
  3. 継続的改善: 発見された問題を次回テスト計画に反映
  4. 自動化と探索のバランス: 定型テストの自動化と創造的な探索テストの併用
  5. テスト技術の多様化: 複数の技法(境界値分析、同値分割、原因-結果など)の組み合わせ
  6. 実用主義: 理論的な完全性より実用的な効果を重視
  7. 生成AIの積極活用: 定型的なテストケース生成や分析に生成AIを活用
  8. 知見の蓄積: 発見されたエッジケース・コーナーケースのライブラリ化と共有
  9. 業種特性の考慮: 自社システムの業種特性に合わせたテスト戦略の最適化
  10. 適応的アプローチ: プロジェクトの制約とリスクに応じてテスト戦略を柔軟に調整
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?